File indexing completed on 2024-05-12 15:27:37
0001 /*************************************************************************** 0002 File : MatrixView.cpp 0003 Project : LabPlot 0004 Description : View class for Matrix 0005 -------------------------------------------------------------------- 0006 Copyright : (C) 2008-2009 Tilman Benkert (thzs@gmx.net) 0007 Copyright : (C) 2015-2019 Alexander Semke (alexander.semke@web.de) 0008 Copyright : (C) 2017 Stefan Gerlach (stefan.gerlach@uni.kn) 0009 0010 ***************************************************************************/ 0011 0012 /*************************************************************************** 0013 * * 0014 * This program is free software; you can redistribute it and/or modify * 0015 * it under the terms of the GNU General Public License as published by * 0016 * the Free Software Foundation; either version 2 of the License, or * 0017 * (at your option) any later version. * 0018 * * 0019 * This program is distributed in the hope that it will be useful, * 0020 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0022 * GNU General Public License for more details. * 0023 * * 0024 * You should have received a copy of the GNU General Public License * 0025 * along with this program; if not, write to the Free Software * 0026 * Foundation, Inc., 51 Franklin Street, Fifth Floor, * 0027 * Boston, MA 02110-1301 USA * 0028 * * 0029 ***************************************************************************/ 0030 0031 0032 #include "commonfrontend/matrix/MatrixView.h" 0033 #include "backend/matrix/Matrix.h" 0034 #include "backend/matrix/MatrixModel.h" 0035 #include "backend/matrix/matrixcommands.h" 0036 #include "backend/lib/macros.h" 0037 #include "backend/core/column/Column.h" 0038 #include "backend/core/column/ColumnPrivate.h" 0039 0040 #include "kdefrontend/spreadsheet/AddSubtractValueDialog.h" 0041 #include "kdefrontend/matrix/MatrixFunctionDialog.h" 0042 #include "kdefrontend/spreadsheet/StatisticsDialog.h" 0043 0044 #include <KLocalizedString> 0045 0046 #include <QAction> 0047 #include <QStackedWidget> 0048 #include <QTableView> 0049 #include <QKeyEvent> 0050 #include <QMenu> 0051 #include <QPainter> 0052 #include <QPrinter> 0053 #include <QScrollArea> 0054 #include <QInputDialog> 0055 #include <QClipboard> 0056 #include <QMimeData> 0057 #include <QTextStream> 0058 #include <QThreadPool> 0059 #include <QMutex> 0060 #include <QProcess> 0061 #include <QHeaderView> 0062 #include <QIcon> 0063 0064 #include <cfloat> 0065 #include <cmath> 0066 0067 MatrixView::MatrixView(Matrix* matrix) : QWidget(), 0068 m_stackedWidget(new QStackedWidget(this)), 0069 m_tableView(new QTableView(this)), 0070 m_imageLabel(new QLabel(this)), 0071 m_matrix(matrix), 0072 m_model(new MatrixModel(matrix)) { 0073 0074 init(); 0075 0076 //resize the view to show a 10x10 region of the matrix. 0077 //no need to resize the view when the project is being opened, 0078 //all views will be resized to the stored values at the end 0079 if (!m_matrix->isLoading()) { 0080 int w = m_tableView->horizontalHeader()->sectionSize(0)*10 + m_tableView->verticalHeader()->width(); 0081 int h = m_tableView->verticalHeader()->sectionSize(0)*10 + m_tableView->horizontalHeader()->height(); 0082 resize(w+50, h+50); 0083 } 0084 } 0085 0086 MatrixView::~MatrixView() { 0087 delete m_model; 0088 } 0089 0090 MatrixModel* MatrixView::model() const { 0091 return m_model; 0092 } 0093 0094 void MatrixView::init() { 0095 initActions(); 0096 connectActions(); 0097 initMenus(); 0098 0099 auto* layout = new QHBoxLayout(this); 0100 layout->setContentsMargins(0, 0, 0, 0); 0101 setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding)); 0102 setFocusPolicy(Qt::StrongFocus); 0103 setFocus(); 0104 installEventFilter(this); 0105 0106 layout->addWidget(m_stackedWidget); 0107 0108 //table data view 0109 m_tableView->setModel(m_model); 0110 m_stackedWidget->addWidget(m_tableView); 0111 0112 //horizontal header 0113 QHeaderView* h_header = m_tableView->horizontalHeader(); 0114 h_header->setSectionsMovable(false); 0115 h_header->installEventFilter(this); 0116 0117 //vertical header 0118 QHeaderView* v_header = m_tableView->verticalHeader(); 0119 v_header->setSectionsMovable(false); 0120 v_header->installEventFilter(this); 0121 0122 //set the header sizes to the (potentially user customized) sizes stored in Matrix 0123 adjustHeaders(); 0124 0125 //image view 0126 auto* area = new QScrollArea(this); 0127 m_stackedWidget->addWidget(area); 0128 area->setWidget(m_imageLabel); 0129 0130 //SLOTs 0131 connect(m_matrix, &Matrix::requestProjectContextMenu, this, &MatrixView::createContextMenu); 0132 connect(m_model, &MatrixModel::changed, this, &MatrixView::matrixDataChanged); 0133 0134 //keyboard shortcuts 0135 sel_all = new QShortcut(QKeySequence(tr("Ctrl+A", "Matrix: select all")), m_tableView); 0136 connect(sel_all, &QShortcut::activated, m_tableView, &QTableView::selectAll); 0137 0138 //TODO: add shortcuts for copy&paste, 0139 //for a single shortcut we need to descriminate between copy&paste for columns, rows or selected cells. 0140 } 0141 0142 void MatrixView::initActions() { 0143 // selection related actions 0144 action_cut_selection = new QAction(QIcon::fromTheme("edit-cut"), i18n("Cu&t"), this); 0145 action_copy_selection = new QAction(QIcon::fromTheme("edit-copy"), i18n("&Copy"), this); 0146 action_paste_into_selection = new QAction(QIcon::fromTheme("edit-paste"), i18n("Past&e"), this); 0147 action_clear_selection = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Selection"), this); 0148 action_select_all = new QAction(QIcon::fromTheme("edit-select-all"), i18n("Select All"), this); 0149 0150 // matrix related actions 0151 auto* viewActionGroup = new QActionGroup(this); 0152 viewActionGroup->setExclusive(true); 0153 action_data_view = new QAction(QIcon::fromTheme("labplot-matrix"), i18n("Data"), viewActionGroup); 0154 action_data_view->setCheckable(true); 0155 action_data_view->setChecked(true); 0156 action_image_view = new QAction(QIcon::fromTheme("image-x-generic"), i18n("Image"), viewActionGroup); 0157 action_image_view->setCheckable(true); 0158 connect(viewActionGroup, &QActionGroup::triggered, this, &MatrixView::switchView); 0159 0160 action_fill_function = new QAction(QIcon::fromTheme(QString()), i18n("Function Values"), this); 0161 action_fill_const = new QAction(QIcon::fromTheme(QString()), i18n("Const Values"), this); 0162 action_clear_matrix = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clear Matrix"), this); 0163 action_go_to_cell = new QAction(QIcon::fromTheme("go-jump"), i18n("&Go to Cell"), this); 0164 0165 action_transpose = new QAction(i18n("&Transpose"), this); 0166 action_mirror_horizontally = new QAction(QIcon::fromTheme("object-flip-horizontal"), i18n("Mirror &Horizontally"), this); 0167 action_mirror_vertically = new QAction(QIcon::fromTheme("object-flip-vertical"), i18n("Mirror &Vertically"), this); 0168 0169 action_add_value = new QAction(i18n("Add Value"), this); 0170 action_add_value->setData(AddSubtractValueDialog::Add); 0171 action_subtract_value = new QAction(i18n("Subtract Value"), this); 0172 action_subtract_value->setData(AddSubtractValueDialog::Subtract); 0173 action_multiply_value = new QAction(i18n("Multiply Value"), this); 0174 action_multiply_value->setData(AddSubtractValueDialog::Multiply); 0175 action_divide_value = new QAction(i18n("Divide Value"), this); 0176 action_divide_value->setData(AddSubtractValueDialog::Divide); 0177 0178 // action_duplicate = new QAction(i18nc("duplicate matrix", "&Duplicate"), this); 0179 //TODO 0180 //icon 0181 auto* headerFormatActionGroup = new QActionGroup(this); 0182 headerFormatActionGroup->setExclusive(true); 0183 action_header_format_1= new QAction(i18n("Rows and Columns"), headerFormatActionGroup); 0184 action_header_format_1->setCheckable(true); 0185 action_header_format_2= new QAction(i18n("xy-Values"), headerFormatActionGroup); 0186 action_header_format_2->setCheckable(true); 0187 action_header_format_3= new QAction(i18n("Rows, Columns and xy-Values"), headerFormatActionGroup); 0188 action_header_format_3->setCheckable(true); 0189 connect(headerFormatActionGroup, &QActionGroup::triggered, this, &MatrixView::headerFormatChanged); 0190 0191 // column related actions 0192 action_add_columns = new QAction(QIcon::fromTheme("edit-table-insert-column-right"), i18n("&Add Columns"), this); 0193 action_insert_columns = new QAction(QIcon::fromTheme("edit-table-insert-column-left"), i18n("&Insert Empty Columns"), this); 0194 action_remove_columns = new QAction(QIcon::fromTheme("edit-table-delete-column"), i18n("Remo&ve Columns"), this); 0195 action_clear_columns = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Columns"), this); 0196 action_statistics_columns = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this); 0197 0198 // row related actions 0199 action_add_rows = new QAction(QIcon::fromTheme("edit-table-insert-row-above"), i18n("&Add Rows"), this); 0200 action_insert_rows = new QAction(QIcon::fromTheme("edit-table-insert-row-above") ,i18n("&Insert Empty Rows"), this); 0201 action_remove_rows = new QAction(QIcon::fromTheme("edit-table-delete-row"), i18n("Remo&ve Rows"), this); 0202 action_clear_rows = new QAction(QIcon::fromTheme("edit-clear"), i18n("Clea&r Rows"), this); 0203 action_statistics_rows = new QAction(QIcon::fromTheme("view-statistics"), i18n("Statisti&cs"), this); 0204 } 0205 0206 void MatrixView::modifyValues() { 0207 const QAction* action = dynamic_cast<const QAction*>(QObject::sender()); 0208 AddSubtractValueDialog::Operation op = (AddSubtractValueDialog::Operation)action->data().toInt(); 0209 auto* dlg = new AddSubtractValueDialog(m_matrix, op); 0210 dlg->setMatrices(); 0211 dlg->exec(); 0212 } 0213 0214 void MatrixView::connectActions() { 0215 // selection related actions 0216 connect(action_cut_selection, &QAction::triggered, this, &MatrixView::cutSelection); 0217 connect(action_copy_selection, &QAction::triggered, this, &MatrixView::copySelection); 0218 connect(action_paste_into_selection, &QAction::triggered, this, &MatrixView::pasteIntoSelection); 0219 connect(action_clear_selection, &QAction::triggered, this, &MatrixView::clearSelectedCells); 0220 connect(action_select_all, &QAction::triggered, m_tableView, &QTableView::selectAll); 0221 0222 // matrix related actions 0223 connect(action_fill_function, &QAction::triggered, this, &MatrixView::fillWithFunctionValues); 0224 connect(action_fill_const, &QAction::triggered, this, &MatrixView::fillWithConstValues); 0225 0226 connect(action_go_to_cell, &QAction::triggered, this, QOverload<>::of(&MatrixView::goToCell)); 0227 //connect(action_duplicate, &QAction::triggered, this, &MatrixView::duplicate); 0228 connect(action_clear_matrix, &QAction::triggered, m_matrix, &Matrix::clear); 0229 connect(action_transpose, &QAction::triggered, m_matrix, &Matrix::transpose); 0230 connect(action_mirror_horizontally, &QAction::triggered, m_matrix, &Matrix::mirrorHorizontally); 0231 connect(action_mirror_vertically, &QAction::triggered, m_matrix, &Matrix::mirrorVertically); 0232 connect(action_add_value, &QAction::triggered, this, &MatrixView::modifyValues); 0233 connect(action_subtract_value, &QAction::triggered, this, &MatrixView::modifyValues); 0234 connect(action_multiply_value, &QAction::triggered, this, &MatrixView::modifyValues); 0235 connect(action_divide_value, &QAction::triggered, this, &MatrixView::modifyValues); 0236 0237 // column related actions 0238 connect(action_add_columns, &QAction::triggered, this, &MatrixView::addColumns); 0239 connect(action_insert_columns, &QAction::triggered, this, &MatrixView::insertEmptyColumns); 0240 connect(action_remove_columns, &QAction::triggered, this, &MatrixView::removeSelectedColumns); 0241 connect(action_clear_columns, &QAction::triggered, this, &MatrixView::clearSelectedColumns); 0242 connect(action_statistics_columns, &QAction::triggered, this, &MatrixView::showColumnStatistics); 0243 0244 // row related actions 0245 connect(action_add_rows, &QAction::triggered, this, &MatrixView::addRows); 0246 connect(action_insert_rows, &QAction::triggered, this, &MatrixView::insertEmptyRows); 0247 connect(action_remove_rows, &QAction::triggered, this, &MatrixView::removeSelectedRows); 0248 connect(action_clear_rows, &QAction::triggered, this, &MatrixView::clearSelectedRows); 0249 connect(action_statistics_rows, &QAction::triggered, this, &MatrixView::showRowStatistics); 0250 } 0251 0252 void MatrixView::initMenus() { 0253 //selection menu 0254 m_selectionMenu = new QMenu(i18n("Selection"), this); 0255 m_selectionMenu->setIcon(QIcon::fromTheme("selection")); 0256 m_selectionMenu->addAction(action_cut_selection); 0257 m_selectionMenu->addAction(action_copy_selection); 0258 m_selectionMenu->addAction(action_paste_into_selection); 0259 m_selectionMenu->addAction(action_clear_selection); 0260 0261 //column menu 0262 m_columnMenu = new QMenu(this); 0263 m_columnMenu->addAction(action_insert_columns); 0264 m_columnMenu->addAction(action_remove_columns); 0265 m_columnMenu->addAction(action_clear_columns); 0266 m_columnMenu->addAction(action_statistics_columns); 0267 0268 //row menu 0269 m_rowMenu = new QMenu(this); 0270 m_rowMenu->addAction(action_insert_rows); 0271 m_rowMenu->addAction(action_remove_rows); 0272 m_rowMenu->addAction(action_clear_rows); 0273 m_rowMenu->addAction(action_statistics_rows); 0274 0275 //matrix menu 0276 m_matrixMenu = new QMenu(this); 0277 0278 m_matrixMenu->addMenu(m_selectionMenu); 0279 m_matrixMenu->addSeparator(); 0280 0281 QMenu* submenu = new QMenu(i18n("Generate Data"), this); 0282 submenu->addAction(action_fill_const); 0283 submenu->addAction(action_fill_function); 0284 m_matrixMenu->addMenu(submenu); 0285 m_matrixMenu->addSeparator(); 0286 0287 // Data manipulation sub-menu 0288 QMenu* dataManipulationMenu = new QMenu(i18n("Manipulate Data"), this); 0289 dataManipulationMenu->addAction(action_add_value); 0290 dataManipulationMenu->addAction(action_subtract_value); 0291 dataManipulationMenu->addAction(action_multiply_value); 0292 dataManipulationMenu->addAction(action_divide_value); 0293 dataManipulationMenu->addSeparator(); 0294 dataManipulationMenu->addAction(action_mirror_horizontally); 0295 dataManipulationMenu->addAction(action_mirror_vertically); 0296 dataManipulationMenu->addSeparator(); 0297 dataManipulationMenu->addAction(action_transpose); 0298 0299 m_matrixMenu->addMenu(dataManipulationMenu); 0300 m_matrixMenu->addSeparator(); 0301 0302 submenu = new QMenu(i18n("View"), this); 0303 submenu->setIcon(QIcon::fromTheme("view-choose")); 0304 submenu->addAction(action_data_view); 0305 submenu->addAction(action_image_view); 0306 m_matrixMenu->addMenu(submenu); 0307 m_matrixMenu->addSeparator(); 0308 0309 m_matrixMenu->addAction(action_select_all); 0310 m_matrixMenu->addAction(action_clear_matrix); 0311 m_matrixMenu->addSeparator(); 0312 0313 m_headerFormatMenu = new QMenu(i18n("Header Format"), this); 0314 m_headerFormatMenu->setIcon(QIcon::fromTheme("format-border-style")); 0315 m_headerFormatMenu->addAction(action_header_format_1); 0316 m_headerFormatMenu->addAction(action_header_format_2); 0317 m_headerFormatMenu->addAction(action_header_format_3); 0318 0319 m_matrixMenu->addMenu(m_headerFormatMenu); 0320 m_matrixMenu->addSeparator(); 0321 m_matrixMenu->addAction(action_go_to_cell); 0322 } 0323 0324 /*! 0325 * Populates the menu \c menu with the spreadsheet and spreadsheet view relevant actions. 0326 * The menu is used 0327 * - as the context menu in MatrixView 0328 * - as the "matrix menu" in the main menu-bar (called form MainWin) 0329 * - as a part of the matrix context menu in project explorer 0330 */ 0331 void MatrixView::createContextMenu(QMenu* menu) const { 0332 Q_ASSERT(menu); 0333 0334 QAction* firstAction = nullptr; 0335 // if we're populating the context menu for the project explorer, then 0336 //there're already actions available there. Skip the first title-action 0337 //and insert the action at the beginning of the menu. 0338 if (menu->actions().size()>1) 0339 firstAction = menu->actions().at(1); 0340 0341 menu->insertMenu(firstAction, m_selectionMenu); 0342 menu->insertSeparator(firstAction); 0343 0344 QMenu* submenu = new QMenu(i18n("Generate Data"), const_cast<MatrixView*>(this)); 0345 submenu->addAction(action_fill_const); 0346 submenu->addAction(action_fill_function); 0347 menu->insertMenu(firstAction, submenu); 0348 menu->insertSeparator(firstAction); 0349 0350 // Data manipulation sub-menu 0351 submenu = new QMenu(i18n("Manipulate Data"), const_cast<MatrixView*>(this)); 0352 submenu->addAction(action_transpose); 0353 submenu->addAction(action_mirror_horizontally); 0354 submenu->addAction(action_mirror_vertically); 0355 submenu->addAction(action_add_value); 0356 submenu->addAction(action_subtract_value); 0357 submenu->addAction(action_multiply_value); 0358 submenu->addAction(action_divide_value); 0359 0360 menu->insertMenu(firstAction, submenu); 0361 menu->insertSeparator(firstAction); 0362 0363 submenu = new QMenu(i18n("View"), const_cast<MatrixView*>(this)); 0364 submenu->addAction(action_data_view); 0365 submenu->addAction(action_image_view); 0366 menu->insertMenu(firstAction, submenu); 0367 menu->insertSeparator(firstAction); 0368 0369 menu->insertAction(firstAction, action_select_all); 0370 menu->insertAction(firstAction, action_clear_matrix); 0371 menu->insertSeparator(firstAction); 0372 // menu->insertAction(firstAction, action_duplicate); 0373 menu->insertMenu(firstAction, m_headerFormatMenu); 0374 0375 menu->insertSeparator(firstAction); 0376 menu->insertAction(firstAction, action_go_to_cell); 0377 menu->insertSeparator(firstAction); 0378 } 0379 0380 /*! 0381 set the row and column size to the saved sizes. 0382 */ 0383 void MatrixView::adjustHeaders() { 0384 QHeaderView* h_header = m_tableView->horizontalHeader(); 0385 QHeaderView* v_header = m_tableView->verticalHeader(); 0386 0387 disconnect(v_header, &QHeaderView::sectionResized, this, &MatrixView::handleVerticalSectionResized); 0388 disconnect(h_header, &QHeaderView::sectionResized, this, &MatrixView::handleHorizontalSectionResized); 0389 0390 //resize columns to the saved sizes or to fit the contents if the widht is 0 0391 int cols = m_matrix->columnCount(); 0392 for (int i = 0; i < cols; i++) { 0393 if (m_matrix->columnWidth(i) == 0) 0394 m_tableView->resizeColumnToContents(i); 0395 else 0396 m_tableView->setColumnWidth(i, m_matrix->columnWidth(i)); 0397 } 0398 0399 //resize rows to the saved sizes or to fit the contents if the height is 0 0400 int rows = m_matrix->rowCount(); 0401 for (int i = 0; i < rows; i++) { 0402 if (m_matrix->rowHeight(i) == 0) 0403 m_tableView->resizeRowToContents(i); 0404 else 0405 m_tableView->setRowHeight(i, m_matrix->rowHeight(i)); 0406 } 0407 0408 connect(v_header, &QHeaderView::sectionResized, this, &MatrixView::handleVerticalSectionResized); 0409 connect(h_header, &QHeaderView::sectionResized, this, &MatrixView::handleHorizontalSectionResized); 0410 } 0411 0412 /*! 0413 Resizes the headers/columns to fit the new content. Called on changes of the header format in Matrix. 0414 */ 0415 void MatrixView::resizeHeaders() { 0416 m_tableView->resizeColumnsToContents(); 0417 m_tableView->resizeRowsToContents(); 0418 0419 if (m_matrix->headerFormat() == Matrix::HeaderFormat::HeaderRowsColumns) 0420 action_header_format_1->setChecked(true); 0421 else if (m_matrix->headerFormat() == Matrix::HeaderFormat::HeaderValues) 0422 action_header_format_2->setChecked(true); 0423 else 0424 action_header_format_3->setChecked(true); 0425 } 0426 0427 /*! 0428 Returns how many columns are selected. 0429 If full is true, this function only returns the number of fully selected columns. 0430 */ 0431 int MatrixView::selectedColumnCount(bool full) const { 0432 int count = 0; 0433 int cols = m_matrix->columnCount(); 0434 for (int i = 0; i < cols; i++) 0435 if (isColumnSelected(i, full)) count++; 0436 return count; 0437 } 0438 0439 /*! 0440 Returns true if column 'col' is selected; otherwise false. 0441 If full is true, this function only returns true if the whole column is selected. 0442 */ 0443 bool MatrixView::isColumnSelected(int col, bool full) const { 0444 if (full) 0445 return m_tableView->selectionModel()->isColumnSelected(col, QModelIndex()); 0446 else 0447 return m_tableView->selectionModel()->columnIntersectsSelection(col, QModelIndex()); 0448 } 0449 0450 /*! 0451 Return how many rows are (at least partly) selected 0452 If full is true, this function only returns the number of fully selected rows. 0453 */ 0454 int MatrixView::selectedRowCount(bool full) const { 0455 int count = 0; 0456 int rows = m_matrix->rowCount(); 0457 for (int i = 0; i < rows; i++) 0458 if (isRowSelected(i, full)) count++; 0459 return count; 0460 } 0461 0462 /*! 0463 Returns true if row \c row is selected; otherwise false 0464 If full is true, this function only returns true if the whole row is selected. 0465 */ 0466 bool MatrixView::isRowSelected(int row, bool full) const { 0467 if (full) 0468 return m_tableView->selectionModel()->isRowSelected(row, QModelIndex()); 0469 else 0470 return m_tableView->selectionModel()->rowIntersectsSelection(row, QModelIndex()); 0471 } 0472 0473 /*! 0474 Return the index of the first selected column. 0475 If full is true, this function only looks for fully selected columns. 0476 */ 0477 int MatrixView::firstSelectedColumn(bool full) const { 0478 int cols = m_matrix->columnCount(); 0479 for (int i = 0; i < cols; i++) { 0480 if (isColumnSelected(i, full)) 0481 return i; 0482 } 0483 return -1; 0484 } 0485 0486 /*! 0487 Return the index of the last selected column 0488 If full is true, this function only looks for fully selected columns. 0489 */ 0490 int MatrixView::lastSelectedColumn(bool full) const { 0491 int cols = m_matrix->columnCount(); 0492 for (int i = cols-1; i >= 0; i--) 0493 if (isColumnSelected(i, full)) return i; 0494 0495 return -2; 0496 } 0497 0498 /*! 0499 Return the index of the first selected row. 0500 If full is true, this function only looks for fully selected rows. 0501 */ 0502 int MatrixView::firstSelectedRow(bool full) const { 0503 int rows = m_matrix->rowCount(); 0504 for (int i = 0; i < rows; i++) { 0505 if (isRowSelected(i, full)) 0506 return i; 0507 } 0508 return -1; 0509 } 0510 0511 /*! 0512 Return the index of the last selected row 0513 If full is true, this function only looks for fully selected rows. 0514 */ 0515 int MatrixView::lastSelectedRow(bool full) const { 0516 int rows = m_matrix->rowCount(); 0517 for (int i = rows-1; i >= 0; i--) 0518 if (isRowSelected(i, full)) return i; 0519 0520 return -2; 0521 } 0522 0523 bool MatrixView::isCellSelected(int row, int col) const { 0524 if (row < 0 || col < 0 || row >= m_matrix->rowCount() || col >= m_matrix->columnCount()) return false; 0525 0526 return m_tableView->selectionModel()->isSelected(m_model->index(row, col)); 0527 } 0528 0529 void MatrixView::setCellSelected(int row, int col) { 0530 m_tableView->selectionModel()->select(m_model->index(row, col), QItemSelectionModel::Select); 0531 } 0532 0533 void MatrixView::setCellsSelected(int first_row, int first_col, int last_row, int last_col) { 0534 QModelIndex top_left = m_model->index(first_row, first_col); 0535 QModelIndex bottom_right = m_model->index(last_row, last_col); 0536 m_tableView->selectionModel()->select(QItemSelection(top_left, bottom_right), QItemSelectionModel::SelectCurrent); 0537 } 0538 0539 /*! 0540 Determine the current cell (-1 if no cell is designated as the current) 0541 */ 0542 void MatrixView::getCurrentCell(int* row, int* col) const { 0543 QModelIndex index = m_tableView->selectionModel()->currentIndex(); 0544 if (index.isValid()) { 0545 *row = index.row(); 0546 *col = index.column(); 0547 } else { 0548 *row = -1; 0549 *col = -1; 0550 } 0551 } 0552 0553 bool MatrixView::eventFilter(QObject * watched, QEvent * event) { 0554 if (event->type() == QEvent::ContextMenu) { 0555 auto* cm_event = static_cast<QContextMenuEvent*>(event); 0556 QPoint global_pos = cm_event->globalPos(); 0557 if (watched == m_tableView->verticalHeader()) 0558 m_rowMenu->exec(global_pos); 0559 else if (watched == m_tableView->horizontalHeader()) 0560 m_columnMenu->exec(global_pos); 0561 else if (watched == this) 0562 m_matrixMenu->exec(global_pos); 0563 else 0564 return QWidget::eventFilter(watched, event); 0565 return true; 0566 } else 0567 return QWidget::eventFilter(watched, event); 0568 } 0569 0570 void MatrixView::keyPressEvent(QKeyEvent* event) { 0571 if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) 0572 advanceCell(); 0573 else if (event->key() == Qt::Key_Backspace || event->matches(QKeySequence::Delete)) 0574 clearSelectedCells(); 0575 } 0576 0577 //############################################################################## 0578 //#################################### SLOTs ################################ 0579 //############################################################################## 0580 /*! 0581 Advance current cell after [Return] or [Enter] was pressed 0582 */ 0583 void MatrixView::advanceCell() { 0584 QModelIndex idx = m_tableView->currentIndex(); 0585 if (idx.row()+1 < m_matrix->rowCount()) 0586 m_tableView->setCurrentIndex(idx.sibling(idx.row()+1, idx.column())); 0587 } 0588 0589 void MatrixView::goToCell() { 0590 bool ok; 0591 0592 int col = QInputDialog::getInt(nullptr, i18n("Go to Cell"), i18n("Enter column"), 1, 1, m_matrix->columnCount(), 1, &ok); 0593 if (!ok) return; 0594 0595 int row = QInputDialog::getInt(nullptr, i18n("Go to Cell"), i18n("Enter row"), 1, 1, m_matrix->rowCount(), 1, &ok); 0596 if (!ok) return; 0597 0598 goToCell(row-1, col-1); 0599 } 0600 0601 void MatrixView::goToCell(int row, int col) { 0602 QModelIndex index = m_model->index(row, col); 0603 m_tableView->scrollTo(index); 0604 m_tableView->setCurrentIndex(index); 0605 } 0606 0607 void MatrixView::handleHorizontalSectionResized(int logicalIndex, int oldSize, int newSize) { 0608 Q_UNUSED(oldSize) 0609 m_matrix->setColumnWidth(logicalIndex, newSize); 0610 } 0611 0612 void MatrixView::handleVerticalSectionResized(int logicalIndex, int oldSize, int newSize) { 0613 Q_UNUSED(oldSize) 0614 m_matrix->setRowHeight(logicalIndex, newSize); 0615 } 0616 0617 void MatrixView::fillWithFunctionValues() { 0618 auto* dlg = new MatrixFunctionDialog(m_matrix); 0619 dlg->exec(); 0620 } 0621 0622 void MatrixView::fillWithConstValues() { 0623 bool ok = false; 0624 double value = QInputDialog::getDouble(this, i18n("Fill the matrix with constant value"), 0625 i18n("Value"), 0, -2147483647, 2147483647, 6, &ok); 0626 if (ok) { 0627 WAIT_CURSOR; 0628 auto* newData = static_cast<QVector<QVector<double>>*>(m_matrix->data()); 0629 for (int col = 0; col < m_matrix->columnCount(); ++col) { 0630 for (int row = 0; row < m_matrix->rowCount(); ++row) 0631 (newData->operator[](col))[row] = value; 0632 } 0633 m_matrix->setData(newData); 0634 RESET_CURSOR; 0635 } 0636 } 0637 0638 //############################ selection related slots ######################### 0639 void MatrixView::cutSelection() { 0640 if (firstSelectedRow() < 0) return; 0641 0642 WAIT_CURSOR; 0643 m_matrix->beginMacro(i18n("%1: cut selected cell(s)", m_matrix->name())); 0644 copySelection(); 0645 clearSelectedCells(); 0646 m_matrix->endMacro(); 0647 RESET_CURSOR; 0648 } 0649 0650 void MatrixView::copySelection() { 0651 int first_col = firstSelectedColumn(false); 0652 if (first_col == -1) return; 0653 int last_col = lastSelectedColumn(false); 0654 if (last_col == -2) return; 0655 int first_row = firstSelectedRow(false); 0656 if (first_row == -1) return; 0657 int last_row = lastSelectedRow(false); 0658 if (last_row == -2) return; 0659 int cols = last_col - first_col +1; 0660 int rows = last_row - first_row +1; 0661 0662 WAIT_CURSOR; 0663 QString output_str; 0664 0665 SET_NUMBER_LOCALE 0666 for (int r = 0; r < rows; r++) { 0667 for (int c = 0; c < cols; c++) { 0668 //TODO: mode 0669 if (isCellSelected(first_row + r, first_col + c)) 0670 output_str += numberLocale.toString(m_matrix->cell<double>(first_row + r, first_col + c), 0671 m_matrix->numericFormat(), 16); // copy with max. precision 0672 if (c < cols-1) 0673 output_str += '\t'; 0674 } 0675 if (r < rows-1) 0676 output_str += '\n'; 0677 } 0678 QApplication::clipboard()->setText(output_str); 0679 RESET_CURSOR; 0680 } 0681 0682 void MatrixView::pasteIntoSelection() { 0683 if (m_matrix->columnCount() < 1 || m_matrix->rowCount() < 1) return; 0684 0685 const QMimeData* mime_data = QApplication::clipboard()->mimeData(); 0686 if (!mime_data->hasFormat("text/plain")) 0687 return; 0688 0689 WAIT_CURSOR; 0690 m_matrix->beginMacro(i18n("%1: paste from clipboard", m_matrix->name())); 0691 0692 int first_col = firstSelectedColumn(false); 0693 int last_col = lastSelectedColumn(false); 0694 int first_row = firstSelectedRow(false); 0695 int last_row = lastSelectedRow(false); 0696 int input_row_count = 0; 0697 int input_col_count = 0; 0698 int rows, cols; 0699 0700 QString input_str = QString(mime_data->data("text/plain")); 0701 QList< QStringList > cell_texts; 0702 QStringList input_rows(input_str.split('\n')); 0703 input_row_count = input_rows.count(); 0704 input_col_count = 0; 0705 for (int i = 0; i < input_row_count; i++) { 0706 cell_texts.append(input_rows.at(i).split('\t')); 0707 if (cell_texts.at(i).count() > input_col_count) input_col_count = cell_texts.at(i).count(); 0708 } 0709 0710 // if the is no selection or only one cell selected, the 0711 // selection will be expanded to the needed size from the current cell 0712 if ( (first_col == -1 || first_row == -1) || 0713 (last_row == first_row && last_col == first_col) ) { 0714 int current_row, current_col; 0715 getCurrentCell(¤t_row, ¤t_col); 0716 if (current_row == -1) current_row = 0; 0717 if (current_col == -1) current_col = 0; 0718 setCellSelected(current_row, current_col); 0719 first_col = current_col; 0720 first_row = current_row; 0721 last_row = first_row + input_row_count -1; 0722 last_col = first_col + input_col_count -1; 0723 // resize the matrix if necessary 0724 if (last_col >= m_matrix->columnCount()) 0725 m_matrix->appendColumns(last_col+1-m_matrix->columnCount()); 0726 if (last_row >= m_matrix->rowCount()) 0727 m_matrix->appendRows(last_row+1-m_matrix->rowCount()); 0728 // select the rectangle to be pasted in 0729 setCellsSelected(first_row, first_col, last_row, last_col); 0730 } 0731 0732 rows = last_row - first_row + 1; 0733 cols = last_col - first_col + 1; 0734 for (int r = 0; r < rows && r < input_row_count; r++) { 0735 for (int c = 0; c < cols && c < input_col_count; c++) { 0736 if (isCellSelected(first_row + r, first_col + c) && (c < cell_texts.at(r).count()) ) 0737 m_matrix->setCell(first_row + r, first_col + c, cell_texts.at(r).at(c).toDouble()); 0738 } 0739 } 0740 0741 m_matrix->endMacro(); 0742 RESET_CURSOR; 0743 } 0744 0745 void MatrixView::clearSelectedCells() { 0746 int first_row = firstSelectedRow(); 0747 if (first_row<0) 0748 return; 0749 0750 int first_col = firstSelectedColumn(); 0751 if (first_col<0) 0752 return; 0753 0754 int last_row = lastSelectedRow(); 0755 int last_col = lastSelectedColumn(); 0756 0757 WAIT_CURSOR; 0758 m_matrix->beginMacro(i18n("%1: clear selected cell(s)", m_matrix->name())); 0759 for (int i = first_row; i <= last_row; i++) { 0760 for (int j = first_col; j <= last_col; j++) { 0761 if (isCellSelected(i, j)) 0762 m_matrix->clearCell(i, j); 0763 } 0764 } 0765 m_matrix->endMacro(); 0766 RESET_CURSOR; 0767 } 0768 0769 class UpdateImageTask : public QRunnable { 0770 public: 0771 UpdateImageTask(int start, int end, QImage& image, const void* data, double scaleFactor, double min) : m_image(image), m_data(data) { 0772 m_start = start; 0773 m_end = end; 0774 m_scaleFactor = scaleFactor; 0775 m_min = min; 0776 }; 0777 0778 void run() override { 0779 for (int row = m_start; row < m_end; ++row) { 0780 m_mutex.lock(); 0781 QRgb* line = reinterpret_cast<QRgb*>(m_image.scanLine(row)); 0782 m_mutex.unlock(); 0783 for (int col = 0; col < m_image.width(); ++col) { 0784 const int gray = (static_cast<const QVector<QVector<double>>*>(m_data)->at(col).at(row)-m_min)*m_scaleFactor; 0785 line[col] = qRgb(gray, gray, gray); 0786 } 0787 } 0788 } 0789 0790 private: 0791 QMutex m_mutex; 0792 int m_start; 0793 int m_end; 0794 QImage& m_image; 0795 const void* m_data; 0796 double m_scaleFactor; 0797 double m_min; 0798 }; 0799 0800 void MatrixView::updateImage() { 0801 WAIT_CURSOR; 0802 m_image = QImage(m_matrix->columnCount(), m_matrix->rowCount(), QImage::Format_ARGB32); 0803 0804 //find min/max value 0805 double dmax = -DBL_MAX, dmin = DBL_MAX; 0806 const QVector<QVector<double>>* data = static_cast<QVector<QVector<double>>*>(m_matrix->data()); 0807 const int width = m_matrix->columnCount(); 0808 const int height = m_matrix->rowCount(); 0809 for (int col = 0; col < width; ++col) { 0810 for (int row = 0; row < height; ++row) { 0811 const double value = (data->operator[](col))[row]; 0812 if (dmax < value) dmax = value; 0813 if (dmin > value) dmin = value; 0814 } 0815 } 0816 0817 //update the image 0818 const double scaleFactor = 255.0/(dmax-dmin); 0819 QThreadPool* pool = QThreadPool::globalInstance(); 0820 int range = ceil(double(m_image.height())/pool->maxThreadCount()); 0821 for (int i = 0; i < pool->maxThreadCount(); ++i) { 0822 const int start = i*range; 0823 int end = (i+1)*range; 0824 if (end > m_image.height()) end = m_image.height(); 0825 auto* task = new UpdateImageTask(start, end, m_image, data, scaleFactor, dmin); 0826 pool->start(task); 0827 } 0828 pool->waitForDone(); 0829 0830 m_imageLabel->resize(width, height); 0831 m_imageLabel->setPixmap(QPixmap::fromImage(m_image)); 0832 m_imageIsDirty = false; 0833 RESET_CURSOR; 0834 } 0835 0836 //############################# matrix related slots ########################### 0837 void MatrixView::switchView(QAction* action) { 0838 if (action == action_data_view) 0839 m_stackedWidget->setCurrentIndex(0); 0840 else { 0841 if (m_imageIsDirty) 0842 this->updateImage(); 0843 0844 m_stackedWidget->setCurrentIndex(1); 0845 } 0846 } 0847 0848 void MatrixView::matrixDataChanged() { 0849 m_imageIsDirty = true; 0850 if (m_stackedWidget->currentIndex() == 1) 0851 this->updateImage(); 0852 } 0853 0854 void MatrixView::headerFormatChanged(QAction* action) { 0855 if (action == action_header_format_1) 0856 m_matrix->setHeaderFormat(Matrix::HeaderFormat::HeaderRowsColumns); 0857 else if (action == action_header_format_2) 0858 m_matrix->setHeaderFormat(Matrix::HeaderFormat::HeaderValues); 0859 else 0860 m_matrix->setHeaderFormat(Matrix::HeaderFormat::HeaderRowsColumnsValues); 0861 } 0862 0863 //############################# column related slots ########################### 0864 /*! 0865 Append as many columns as are selected. 0866 */ 0867 void MatrixView::addColumns() { 0868 m_matrix->appendColumns(selectedColumnCount(false)); 0869 } 0870 0871 void MatrixView::insertEmptyColumns() { 0872 int first = firstSelectedColumn(); 0873 int last = lastSelectedColumn(); 0874 if (first < 0) return; 0875 int current = first; 0876 0877 WAIT_CURSOR; 0878 m_matrix->beginMacro(i18n("%1: insert empty column(s)", m_matrix->name())); 0879 while (current <= last) { 0880 current = first+1; 0881 while (current <= last && isColumnSelected(current)) current++; 0882 const int count = current-first; 0883 m_matrix->insertColumns(first, count); 0884 current += count; 0885 last += count; 0886 while (current <= last && isColumnSelected(current)) current++; 0887 first = current; 0888 } 0889 m_matrix->endMacro(); 0890 RESET_CURSOR; 0891 } 0892 0893 void MatrixView::removeSelectedColumns() { 0894 int first = firstSelectedColumn(); 0895 int last = lastSelectedColumn(); 0896 if (first < 0) return; 0897 0898 WAIT_CURSOR; 0899 m_matrix->beginMacro(i18n("%1: remove selected column(s)", m_matrix->name())); 0900 for (int i = last; i >= first; i--) 0901 if (isColumnSelected(i, false)) m_matrix->removeColumns(i, 1); 0902 m_matrix->endMacro(); 0903 RESET_CURSOR; 0904 } 0905 0906 void MatrixView::clearSelectedColumns() { 0907 WAIT_CURSOR; 0908 m_matrix->beginMacro(i18n("%1: clear selected column(s)", m_matrix->name())); 0909 for (int i = 0; i < m_matrix->columnCount(); i++) { 0910 if (isColumnSelected(i, false)) 0911 m_matrix->clearColumn(i); 0912 } 0913 m_matrix->endMacro(); 0914 RESET_CURSOR; 0915 } 0916 0917 //############################## rows related slots ############################ 0918 /*! 0919 Append as many rows as are selected. 0920 */ 0921 void MatrixView::addRows() { 0922 m_matrix->appendRows(selectedRowCount(false)); 0923 } 0924 0925 void MatrixView::insertEmptyRows() { 0926 int first = firstSelectedRow(); 0927 int last = lastSelectedRow(); 0928 int current = first; 0929 0930 if (first < 0) return; 0931 0932 WAIT_CURSOR; 0933 m_matrix->beginMacro(i18n("%1: insert empty rows(s)", m_matrix->name())); 0934 while (current <= last) { 0935 current = first+1; 0936 while (current <= last && isRowSelected(current)) current++; 0937 const int count = current-first; 0938 m_matrix->insertRows(first, count); 0939 current += count; 0940 last += count; 0941 while (current <= last && !isRowSelected(current)) current++; 0942 first = current; 0943 } 0944 m_matrix->endMacro(); 0945 RESET_CURSOR; 0946 } 0947 0948 void MatrixView::removeSelectedRows() { 0949 int first = firstSelectedRow(); 0950 int last = lastSelectedRow(); 0951 if (first < 0) return; 0952 0953 WAIT_CURSOR; 0954 m_matrix->beginMacro(i18n("%1: remove selected rows(s)", m_matrix->name())); 0955 for (int i = last; i >= first; i--) 0956 if (isRowSelected(i, false)) m_matrix->removeRows(i, 1); 0957 m_matrix->endMacro(); 0958 RESET_CURSOR; 0959 } 0960 0961 void MatrixView::clearSelectedRows() { 0962 int first = firstSelectedRow(); 0963 int last = lastSelectedRow(); 0964 if (first < 0) return; 0965 0966 WAIT_CURSOR; 0967 m_matrix->beginMacro(i18n("%1: clear selected rows(s)", m_matrix->name())); 0968 for (int i = first; i <= last; i++) { 0969 if (isRowSelected(i)) 0970 m_matrix->clearRow(i); 0971 } 0972 m_matrix->endMacro(); 0973 RESET_CURSOR; 0974 } 0975 0976 /*! 0977 prints the complete matrix to \c printer. 0978 */ 0979 0980 void MatrixView::print(QPrinter* printer) const { 0981 WAIT_CURSOR; 0982 QPainter painter (printer); 0983 0984 const int dpiy = printer->logicalDpiY(); 0985 const int margin = (int) ( (1/2.54)*dpiy ); // 1 cm margins 0986 0987 QHeaderView* hHeader = m_tableView->horizontalHeader(); 0988 QHeaderView* vHeader = m_tableView->verticalHeader(); 0989 auto* data = static_cast<QVector<QVector<double>>*>(m_matrix->data()); 0990 0991 const int rows = m_matrix->rowCount(); 0992 const int cols = m_matrix->columnCount(); 0993 int height = margin; 0994 const int vertHeaderWidth = vHeader->width(); 0995 int right = margin + vertHeaderWidth; 0996 0997 int columnsPerTable = 0; 0998 int headerStringWidth = 0; 0999 int firstRowStringWidth = vertHeaderWidth; 1000 bool tablesNeeded = false; 1001 QVector<int> firstRowCeilSizes; 1002 firstRowCeilSizes.reserve(data[0].size()); 1003 firstRowCeilSizes.resize(data[0].size()); 1004 QRect br; 1005 1006 for (int i = 0; i < data->size(); ++i) { 1007 br = painter.boundingRect(br, Qt::AlignCenter,QString::number(data->at(i)[0]) + '\t'); 1008 firstRowCeilSizes[i] = br.width() > m_tableView->columnWidth(i) ? 1009 br.width() : m_tableView->columnWidth(i); 1010 } 1011 for (int col = 0; col < cols; ++col) { 1012 headerStringWidth += m_tableView->columnWidth(col); 1013 br = painter.boundingRect(br, Qt::AlignCenter,QString::number(data->at(col)[0]) + '\t'); 1014 firstRowStringWidth += br.width(); 1015 if ((headerStringWidth >= printer->pageRect().width() -2*margin) || 1016 (firstRowStringWidth >= printer->pageRect().width() - 2*margin)) { 1017 tablesNeeded = true; 1018 break; 1019 } 1020 columnsPerTable++; 1021 } 1022 1023 int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0; 1024 const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols; 1025 1026 if (!tablesNeeded) { 1027 tablesCount = 1; 1028 columnsPerTable = cols; 1029 } 1030 if (remainingColumns > 0) 1031 tablesCount++; 1032 for (int table = 0; table < tablesCount; ++table) { 1033 //Paint the horizontal header first 1034 painter.setFont(hHeader->font()); 1035 QString headerString = m_tableView->model()->headerData(0, Qt::Horizontal).toString(); 1036 QRect br; 1037 br = painter.boundingRect(br, Qt::AlignCenter, headerString); 1038 QRect tr(br); 1039 if (table != 0) 1040 height += tr.height(); 1041 painter.drawLine(right, height, right, height+br.height()); 1042 1043 int i = table * columnsPerTable; 1044 int toI = table * columnsPerTable + columnsPerTable; 1045 if ((remainingColumns > 0) && (table == tablesCount-1)) { 1046 i = (tablesCount-1)*columnsPerTable; 1047 toI = (tablesCount-1)* columnsPerTable + remainingColumns; 1048 } 1049 1050 for (; i<toI; ++i) { 1051 headerString = m_tableView->model()->headerData(i, Qt::Horizontal).toString(); 1052 const int w = /*m_tableView->columnWidth(i)*/ firstRowCeilSizes[i]; 1053 tr.setTopLeft(QPoint(right,height)); 1054 tr.setWidth(w); 1055 tr.setHeight(br.height()); 1056 1057 painter.drawText(tr, Qt::AlignCenter, headerString); 1058 right += w; 1059 painter.drawLine(right, height, right, height+tr.height()); 1060 1061 } 1062 //first horizontal line 1063 painter.drawLine(margin + vertHeaderWidth, height, right-1, height); 1064 height += tr.height(); 1065 painter.drawLine(margin, height, right-1, height); 1066 1067 // print table values 1068 QString cellText; 1069 for (i = 0; i < rows; ++i) { 1070 right = margin; 1071 cellText = m_tableView->model()->headerData(i, Qt::Vertical).toString()+'\t'; 1072 tr = painter.boundingRect(tr, Qt::AlignCenter, cellText); 1073 painter.drawLine(right, height, right, height+tr.height()); 1074 1075 br.setTopLeft(QPoint(right,height)); 1076 br.setWidth(vertHeaderWidth); 1077 br.setHeight(tr.height()); 1078 painter.drawText(br, Qt::AlignCenter, cellText); 1079 right += vertHeaderWidth; 1080 painter.drawLine(right, height, right, height+tr.height()); 1081 int j = table * columnsPerTable; 1082 int toJ = table * columnsPerTable + columnsPerTable; 1083 if ((remainingColumns > 0) && (table == tablesCount-1)) { 1084 j = (tablesCount-1)*columnsPerTable; 1085 toJ = (tablesCount-1)* columnsPerTable + remainingColumns; 1086 } 1087 for (; j< toJ; j++) { 1088 int w = /*m_tableView->columnWidth(j)*/ firstRowCeilSizes[j]; 1089 cellText = QString::number(data->at(j)[i]) + '\t'; 1090 tr = painter.boundingRect(tr,Qt::AlignCenter,cellText); 1091 br.setTopLeft(QPoint(right,height)); 1092 br.setWidth(w); 1093 br.setHeight(tr.height()); 1094 painter.drawText(br, Qt::AlignCenter, cellText); 1095 right += w; 1096 painter.drawLine(right, height, right, height+tr.height()); 1097 } 1098 height += br.height(); 1099 painter.drawLine(margin, height, right-1, height); 1100 1101 if (height >= printer->height()-margin ) { 1102 printer->newPage(); 1103 height = margin; 1104 painter.drawLine(margin, height, right, height); 1105 } 1106 } 1107 } 1108 RESET_CURSOR; 1109 } 1110 1111 void MatrixView::exportToFile(const QString& path, const QString& separator, QLocale::Language language) const { 1112 QFile file(path); 1113 if (!file.open(QFile::WriteOnly | QFile::Truncate)) 1114 return; 1115 1116 QTextStream out(&file); 1117 1118 QString sep = separator; 1119 sep = sep.replace(QLatin1String("TAB"), QLatin1String("\t"), Qt::CaseInsensitive); 1120 sep = sep.replace(QLatin1String("SPACE"), QLatin1String(" "), Qt::CaseInsensitive); 1121 1122 //export values 1123 const int cols = m_matrix->columnCount(); 1124 const int rows = m_matrix->rowCount(); 1125 const QVector<QVector<double> >* data = static_cast<QVector<QVector<double>>*>(m_matrix->data()); 1126 //TODO: use general setting for number locale? 1127 QLocale locale(language); 1128 for (int row = 0; row < rows; ++row) { 1129 for (int col = 0; col < cols; ++col) { 1130 out << locale.toString(data->at(col)[row], m_matrix->numericFormat(), m_matrix->precision()); 1131 1132 out << data->at(col)[row]; 1133 if (col != cols-1) 1134 out << sep; 1135 } 1136 out << '\n'; 1137 } 1138 } 1139 1140 void MatrixView::exportToLaTeX(const QString& path, const bool verticalHeaders, const bool horizontalHeaders, 1141 const bool latexHeaders, const bool gridLines, const bool entire, const bool captions) const { 1142 QFile file(path); 1143 if (!file.open(QFile::WriteOnly | QFile::Truncate)) 1144 return; 1145 1146 QVector<QVector<QString> > toExport; 1147 1148 int firstSelectedCol = 0; 1149 int firstSelectedRowi = 0; 1150 int totalRowCount = 0; 1151 int cols = 0; 1152 if (entire) { 1153 cols = m_matrix->columnCount(); 1154 totalRowCount = m_matrix->rowCount(); 1155 toExport.reserve(totalRowCount); 1156 toExport.resize(totalRowCount); 1157 for (int row = 0; row < totalRowCount; ++row) { 1158 toExport[row].reserve(cols); 1159 toExport[row].resize(cols); 1160 //TODO: mode 1161 for (int col = 0; col < cols; ++col) 1162 toExport[row][col] = m_matrix->text<double>(row,col); 1163 } 1164 firstSelectedCol = 0; 1165 firstSelectedRowi = 0; 1166 } else { 1167 cols = selectedColumnCount(); 1168 totalRowCount = selectedRowCount(); 1169 1170 firstSelectedCol = firstSelectedColumn(); 1171 if (firstSelectedCol == -1) 1172 return; 1173 firstSelectedRowi = firstSelectedRow(); 1174 if (firstSelectedRowi == -1) 1175 return; 1176 const int lastSelectedCol = lastSelectedColumn(); 1177 const int lastSelectedRowi = lastSelectedRow(); 1178 1179 toExport.reserve(lastSelectedRowi - firstSelectedRowi+1); 1180 toExport.resize(lastSelectedRowi - firstSelectedRowi+1); 1181 int r = 0; 1182 for (int row = firstSelectedRowi; row <= lastSelectedRowi; ++row, ++r) { 1183 toExport[r].reserve(lastSelectedCol - firstSelectedCol+1); 1184 toExport[r].resize(lastSelectedCol - firstSelectedCol+1); 1185 int c = 0; 1186 //TODO: mode 1187 for (int col = firstSelectedCol; col <= lastSelectedCol; ++col,++c) 1188 toExport[r][c] = m_matrix->text<double>(row, col); 1189 } 1190 } 1191 1192 int columnsStringSize = 0; 1193 int headerStringSize = 0; 1194 int columnsPerTable = 0; 1195 const int firstHHeaderSectionLength = m_tableView->model()->headerData(0, Qt::Horizontal).toString().length(); 1196 const int firstSelectedVHeaderSectionLength = m_tableView->model()->headerData(firstSelectedRow(), Qt::Vertical).toString().length(); 1197 if (verticalHeaders) { 1198 if (entire) 1199 headerStringSize += firstHHeaderSectionLength; 1200 else 1201 headerStringSize += firstSelectedVHeaderSectionLength; 1202 } 1203 if (!horizontalHeaders && verticalHeaders) { 1204 if (entire) 1205 columnsStringSize += firstHHeaderSectionLength; 1206 else 1207 columnsStringSize += firstSelectedVHeaderSectionLength; 1208 } 1209 1210 for (int col = 0; col < cols; ++col) { 1211 int maxSize = -1; 1212 for (auto row : toExport) { 1213 if (row.at(col).size() > maxSize) 1214 maxSize = row.at(col).size(); 1215 } 1216 columnsStringSize += maxSize; 1217 if (horizontalHeaders) 1218 headerStringSize += m_tableView->model()->headerData(col, Qt::Horizontal).toString().length(); 1219 if ((columnsStringSize > 65) || (headerStringSize > 65)) 1220 break; 1221 ++columnsPerTable; 1222 } 1223 1224 int tablesCount = (columnsPerTable != 0) ? cols/columnsPerTable : 0; 1225 const int remainingColumns = (columnsPerTable != 0) ? cols % columnsPerTable : cols; 1226 1227 bool columnsSeparating = (cols > columnsPerTable); 1228 QTextStream out(&file); 1229 1230 QProcess tex; 1231 tex.start("latex", QStringList() << "--version", QProcess::ReadOnly); 1232 tex.waitForFinished(500); 1233 QString texVersionOutput = QString(tex.readAllStandardOutput()); 1234 texVersionOutput = texVersionOutput.split('\n')[0]; 1235 1236 int yearidx = -1; 1237 for (int i = texVersionOutput.size() - 1; i >= 0; --i) { 1238 if (texVersionOutput.at(i) == QChar('2')) { 1239 yearidx = i; 1240 break; 1241 } 1242 } 1243 1244 if (texVersionOutput.at(yearidx+1) == QChar('/')) 1245 yearidx-=3; 1246 1247 bool ok; 1248 texVersionOutput.midRef(yearidx, 4).toInt(&ok); 1249 int version = -1; 1250 if (ok) 1251 version = texVersionOutput.midRef(yearidx, 4).toInt(&ok); 1252 1253 if (latexHeaders) { 1254 out << QLatin1String("\\documentclass[11pt,a4paper]{article} \n"); 1255 out << QLatin1String("\\usepackage{geometry} \n"); 1256 out << QLatin1String("\\usepackage{xcolor,colortbl} \n"); 1257 if (version >= 2015) 1258 out << QLatin1String("\\extrafloats{1280} \n"); 1259 out << QLatin1String("\\definecolor{HeaderBgColor}{rgb}{0.81,0.81,0.81} \n"); 1260 out << QLatin1String("\\geometry{ \n"); 1261 out << QLatin1String("a4paper, \n"); 1262 out << QLatin1String("total={170mm,257mm}, \n"); 1263 out << QLatin1String("left=10mm, \n"); 1264 out << QLatin1String("top=10mm } \n"); 1265 1266 out << QLatin1String("\\begin{document} \n"); 1267 out << QLatin1String("\\title{LabPlot Matrix Export to \\LaTeX{} } \n"); 1268 out << QLatin1String("\\author{LabPlot} \n"); 1269 out << QLatin1String("\\date{\\today} \n"); 1270 // out << "\\maketitle \n"; 1271 } 1272 1273 const QString endTabularTable ("\\end{tabular} \n \\end{table} \n"); 1274 const QString tableCaption ("\\caption{"+ m_matrix->name() + "} \n"); 1275 const QString beginTable ("\\begin{table}[ht] \n"); 1276 const QString centeredColumn( gridLines ? QLatin1String(" c |") : QLatin1String(" c ")); 1277 int rowCount = 0; 1278 const int maxRows = 45; 1279 bool captionRemoved = false; 1280 1281 if (columnsSeparating) { 1282 for (int table = 0; table < tablesCount; ++table) { 1283 QStringList textable; 1284 captionRemoved = false; 1285 1286 textable << beginTable; 1287 if (captions) 1288 textable << tableCaption; 1289 textable << QLatin1String("\\centering \n"); 1290 textable << QLatin1String("\\begin{tabular}{"); 1291 textable << (gridLines ? QStringLiteral("|") : QString()); 1292 for (int i = 0; i < columnsPerTable; ++i) 1293 textable << centeredColumn; 1294 if (verticalHeaders) 1295 textable << centeredColumn; 1296 textable << QLatin1String("} \n"); 1297 if (gridLines) 1298 textable << QLatin1String("\\hline \n"); 1299 1300 if (horizontalHeaders) { 1301 if (latexHeaders) 1302 textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); 1303 if (verticalHeaders) 1304 textable << QLatin1String(" & "); 1305 for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col) { 1306 textable << m_tableView->model()->headerData(col + firstSelectedCol, Qt::Horizontal).toString(); 1307 if (col != ((table * columnsPerTable)+ columnsPerTable)-1) 1308 textable << QLatin1String(" & "); 1309 } 1310 textable << QLatin1String("\\\\ \n"); 1311 if (gridLines) 1312 textable << QLatin1String("\\hline \n"); 1313 } 1314 for (const auto& s : textable) 1315 out << s; 1316 for (int row = 0; row < totalRowCount; ++row) { 1317 if (verticalHeaders) { 1318 out << "\\cellcolor{HeaderBgColor} "; 1319 out << m_tableView->model()->headerData(row + firstSelectedRowi, Qt::Vertical).toString(); 1320 out << QLatin1String(" & "); 1321 } 1322 for (int col = table*columnsPerTable; col < (table * columnsPerTable) + columnsPerTable; ++col ) { 1323 out << toExport.at(row).at(col); 1324 if (col != ((table * columnsPerTable)+ columnsPerTable)-1) 1325 out << QLatin1String(" & "); 1326 } 1327 1328 out << QLatin1String("\\\\ \n"); 1329 if (gridLines) 1330 out << QLatin1String("\\hline \n"); 1331 rowCount++; 1332 if (rowCount == maxRows) { 1333 out << endTabularTable; 1334 out << QLatin1String("\\newpage \n"); 1335 if (captions) 1336 if (!captionRemoved) 1337 textable.removeAt(1); 1338 for (const auto& s : textable) 1339 out << s; 1340 rowCount = 0; 1341 if (!captionRemoved) 1342 captionRemoved = true; 1343 } 1344 } 1345 out << endTabularTable; 1346 } 1347 captionRemoved = false; 1348 1349 QStringList remainingTable; 1350 remainingTable << beginTable; 1351 if (captions) 1352 remainingTable << tableCaption; 1353 remainingTable << QLatin1String("\\centering \n"); 1354 remainingTable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString()); 1355 for (int c = 0; c < remainingColumns; ++c) 1356 remainingTable << centeredColumn; 1357 if (verticalHeaders) 1358 remainingTable << centeredColumn; 1359 remainingTable << QLatin1String("} \n"); 1360 if (gridLines) 1361 remainingTable << QLatin1String("\\hline \n"); 1362 1363 if (horizontalHeaders) { 1364 if (latexHeaders) 1365 remainingTable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); 1366 if (verticalHeaders) 1367 remainingTable << QLatin1String(" & "); 1368 for (int col = 0; col < remainingColumns; ++col) { 1369 remainingTable << m_tableView->model()->headerData(firstSelectedCol+col + (tablesCount * columnsPerTable), Qt::Horizontal).toString(); 1370 if (col != remainingColumns-1) 1371 remainingTable << QLatin1String(" & "); 1372 } 1373 remainingTable << QLatin1String("\\\\ \n"); 1374 if (gridLines) 1375 remainingTable << QLatin1String("\\hline \n"); 1376 } 1377 1378 for (const auto& s : remainingTable) 1379 out << s; 1380 1381 for (int row = 0; row < totalRowCount; ++row) { 1382 if (verticalHeaders) { 1383 out << "\\cellcolor{HeaderBgColor}"; 1384 out << m_tableView->model()->headerData(row+ firstSelectedRowi, Qt::Vertical).toString(); 1385 out << QLatin1String(" & "); 1386 } 1387 for (int col = 0; col < remainingColumns; ++col ) { 1388 out << toExport.at(row).at(col + (tablesCount * columnsPerTable)); 1389 if (col != remainingColumns-1) 1390 out << QLatin1String(" & "); 1391 } 1392 1393 out << QLatin1String("\\\\ \n"); 1394 if (gridLines) 1395 out << QLatin1String("\\hline \n"); 1396 rowCount++; 1397 if (rowCount == maxRows) { 1398 out << endTabularTable; 1399 out << QLatin1String("\\pagebreak[4] \n"); 1400 if (captions) 1401 if (!captionRemoved) 1402 remainingTable.removeAt(1); 1403 for (const auto& s : remainingTable) 1404 out << s; 1405 rowCount = 0; 1406 if (!captionRemoved) 1407 captionRemoved = true; 1408 } 1409 } 1410 out << endTabularTable; 1411 } else { 1412 QStringList textable; 1413 textable << beginTable; 1414 if (captions) 1415 textable << tableCaption; 1416 textable << QLatin1String("\\centering \n"); 1417 textable << QLatin1String("\\begin{tabular}{") << (gridLines ? QStringLiteral("|") : QString()); 1418 for (int c = 0; c < cols; ++c) 1419 textable << centeredColumn; 1420 if (verticalHeaders) 1421 textable << centeredColumn; 1422 textable << QLatin1String("} \n"); 1423 if (gridLines) 1424 textable << QLatin1String("\\hline \n"); 1425 1426 if (horizontalHeaders) { 1427 if (latexHeaders) 1428 textable << QLatin1String("\\rowcolor{HeaderBgColor} \n"); 1429 if (verticalHeaders) 1430 textable << QLatin1String(" & "); 1431 for (int col = 0; col < cols ; ++col) { 1432 textable << m_tableView->model()->headerData(col+firstSelectedCol, Qt::Horizontal).toString(); 1433 if (col != cols-1) 1434 textable << QLatin1String(" & "); 1435 } 1436 textable << QLatin1String("\\\\ \n"); 1437 if (gridLines) 1438 textable << QLatin1String("\\hline \n"); 1439 } 1440 1441 for (const auto& s : textable) 1442 out << s; 1443 for (int row = 0; row < totalRowCount; ++row) { 1444 if (verticalHeaders) { 1445 out << "\\cellcolor{HeaderBgColor}"; 1446 out << m_tableView->model()->headerData(row + firstSelectedRowi, Qt::Vertical).toString(); 1447 out << QLatin1String(" & "); 1448 } 1449 for (int col = 0; col < cols; ++col ) { 1450 out << toExport.at(row).at(col); 1451 if (col != cols-1) 1452 out << " & "; 1453 } 1454 out << QLatin1String("\\\\ \n"); 1455 if (gridLines) 1456 out << QLatin1String("\\hline \n"); 1457 rowCount++; 1458 if (rowCount == maxRows) { 1459 out << endTabularTable; 1460 out << QLatin1String("\\newpage \n"); 1461 if (captions) 1462 if (!captionRemoved) 1463 textable.removeAt(1); 1464 for (const auto& s : textable) 1465 out << s; 1466 if (!captionRemoved) 1467 captionRemoved = true; 1468 rowCount = 0; 1469 if (!captionRemoved) 1470 captionRemoved = true; 1471 } 1472 } 1473 out << endTabularTable; 1474 } 1475 if (latexHeaders) 1476 out << QLatin1String("\\end{document} \n"); 1477 } 1478 1479 void MatrixView::showColumnStatistics() { 1480 if (selectedColumnCount() > 0) { 1481 QString dlgTitle (m_matrix->name() + " column statistics"); 1482 QVector<Column*> columns; 1483 for (int col = 0; col < m_matrix->columnCount(); ++col) { 1484 if (isColumnSelected(col, false)) { 1485 QString headerString = m_tableView->model()->headerData(col, Qt::Horizontal).toString(); 1486 columns << new Column(headerString, static_cast<QVector<QVector<double>>*>(m_matrix->data())->at(col)); 1487 } 1488 } 1489 auto* dlg = new StatisticsDialog(dlgTitle, columns); 1490 dlg->showStatistics(); 1491 if (dlg->exec() == QDialog::Accepted) { 1492 qDeleteAll(columns); 1493 columns.clear(); 1494 } 1495 } 1496 } 1497 1498 void MatrixView::showRowStatistics() { 1499 if (selectedRowCount() > 0) { 1500 QString dlgTitle (m_matrix->name() + " row statistics"); 1501 QVector<Column*> columns; 1502 for (int row = 0; row < m_matrix->rowCount(); ++row) { 1503 if (isRowSelected(row, false)) { 1504 QString headerString = m_tableView->model()->headerData(row, Qt::Vertical).toString(); 1505 //TODO: mode 1506 columns << new Column(headerString, m_matrix->rowCells<double>(row, 0, m_matrix->columnCount()-1)); 1507 } 1508 } 1509 auto* dlg = new StatisticsDialog(dlgTitle, columns); 1510 dlg->showStatistics(); 1511 if (dlg->exec() == QDialog::Accepted) { 1512 qDeleteAll(columns); 1513 columns.clear(); 1514 } 1515 } 1516 } 1517 1518 void MatrixView::exportToFits(const QString &fileName, const int exportTo) const { 1519 auto* filter = new FITSFilter; 1520 filter->setExportTo(exportTo); 1521 filter->write(fileName, m_matrix); 1522 1523 delete filter; 1524 }