Mantid
Loading...
Searching...
No Matches
FitScriptGeneratorDataTable.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2020 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
9#include <QAbstractItemView>
10#include <QColor>
11#include <QDoubleValidator>
12#include <QHeaderView>
13#include <QHoverEvent>
14#include <QItemSelectionModel>
15#include <QLineEdit>
16#include <QStringList>
17#include <QValidator>
18
19#include <algorithm>
20
21namespace {
22
23int WS_INDEX_MIN(0);
24int WS_INDEX_MAX(100000);
25double X_EXTENT(100000.0);
26int X_PRECISION(5);
27
28QStringList const COLUMN_HEADINGS({"Name", "WS Index", "StartX", "EndX"});
29QColor const FUNCTION_INDEX_COLOR(QColor(30, 144, 255));
30QString const TABLE_STYLESHEET("QTableWidget {\n"
31 " font-size: 8pt;\n"
32 " border: 1px solid #828790;\n"
33 "}\n"
34 "\n"
35 "QTableWidget::item:selected {\n"
36 " background-color: #c7e0ff;\n"
37 " color: #000000;\n"
38 "}"
39 "\n"
40 "QTableWidget::item:hover {\n"
41 " background-color: #c7e0ff;\n"
42 "}");
43
44QValidator *createXValidator() {
45 auto validator = new QDoubleValidator(-X_EXTENT, X_EXTENT, X_PRECISION);
46 validator->setNotation(QDoubleValidator::Notation::StandardNotation);
47 return validator;
48}
49
50QValidator *createWSIndexValidator() { return new QIntValidator(WS_INDEX_MIN, WS_INDEX_MAX); }
51
52QTableWidgetItem *createTableItem(QString const &value, Qt::AlignmentFlag const &alignment, bool editable,
53 QColor const &color = QColor(0, 0, 0)) {
54 auto item = new QTableWidgetItem(value);
55 item->setData(Qt::ForegroundRole, color);
56 item->setTextAlignment(alignment);
57 if (!editable)
58 item->setFlags(item->flags() ^ Qt::ItemIsEditable);
59 return item;
60}
61
62QTableWidgetItem *createWSIndexTableItem(int value, Qt::AlignmentFlag const &alignment, bool editable) {
63 return createTableItem(QString::number(value), alignment, editable);
64}
65
66QTableWidgetItem *createXTableItem(double value, Qt::AlignmentFlag const &alignment, bool editable) {
67 return createTableItem(QString::number(value, 'f', X_PRECISION), alignment, editable);
68}
69
70QString toFunctionIndex(MantidQt::MantidWidgets::FitDomainIndex index) {
71 return "f" + QString::number(index.value) + ".";
72}
73
74} // namespace
75
77
83 : QTableWidget(parent), m_selectedRows(), m_selectedColumn(-1), m_selectedValue(0.0),
84 m_lastHoveredIndex(QPersistentModelIndex()) {
85 this->setSelectionBehavior(QAbstractItemView::SelectRows);
86 this->setSelectionMode(QAbstractItemView::ExtendedSelection);
87 this->setShowGrid(false);
88 this->setColumnCount(COLUMN_HEADINGS.size());
89 this->setRowCount(0);
90 this->horizontalHeader()->setHighlightSections(false);
91 this->horizontalHeader()->setStretchLastSection(true);
92
93 this->setColumnWidth(ColumnIndex::WorkspaceName, 280);
94 this->setColumnWidth(ColumnIndex::WorkspaceIndex, 80);
95 this->setColumnWidth(ColumnIndex::StartX, 100);
96 this->setColumnWidth(ColumnIndex::EndX, 100);
97
98 this->setHorizontalHeaderLabels(COLUMN_HEADINGS);
99
100 this->setStyleSheet(TABLE_STYLESHEET);
101
102 this->viewport()->installEventFilter(this);
103
104 this->setItemDelegateForColumn(ColumnIndex::WorkspaceName, new CustomItemDelegate(this, ColumnIndex::WorkspaceName));
105 this->setItemDelegateForColumn(ColumnIndex::WorkspaceIndex,
106 new CustomItemDelegate(this, ColumnIndex::WorkspaceIndex));
107 this->setItemDelegateForColumn(ColumnIndex::StartX, new CustomItemDelegate(this, ColumnIndex::StartX));
108 this->setItemDelegateForColumn(ColumnIndex::EndX, new CustomItemDelegate(this, ColumnIndex::EndX));
109
110 connect(this, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(handleItemClicked(QTableWidgetItem *)));
111 connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(handleItemSelectionChanged()));
112 disconnect(this->verticalHeader(), SIGNAL(sectionPressed(int)), this, SLOT(selectRow(int)));
113}
114
115bool FitScriptGeneratorDataTable::eventFilter(QObject *widget, QEvent *event) {
116 if (widget == this->viewport()) {
117 auto index = hoveredRowIndex(event);
118
119 if (index != m_lastHoveredIndex) {
120 if (this->item(m_lastHoveredIndex.row(), m_lastHoveredIndex.column()))
121 emit itemExited(index.isValid() ? index.row() : -1);
122 m_lastHoveredIndex = QPersistentModelIndex(index);
123 }
124 }
125 return QTableWidget::eventFilter(widget, event);
126}
127
130 m_selectedColumn = item->column();
131 if (m_selectedColumn == ColumnIndex::StartX || m_selectedColumn == ColumnIndex::EndX)
132 m_selectedValue = item->text().toDouble();
133}
134
136 auto *selectionModel = this->selectionModel();
137
138 if (!selectionModel->hasSelection()) {
139 this->blockSignals(true);
140
141 // Makes sure that multi-selection rows are stored within the selectionModel
142 // as should be expected. This prevents a bug where not all selected rows
143 // were being stored in the selection model.
144 auto itemSelection = selectionModel->selection();
145 for (auto const &selectedRow : m_selectedRows) {
146 this->selectRow(static_cast<int>(selectedRow.value));
147 itemSelection.merge(selectionModel->selection(), QItemSelectionModel::Select);
148 }
149 selectionModel->clearSelection();
150 selectionModel->select(itemSelection, QItemSelectionModel::Select);
151
152 this->blockSignals(false);
153 } else {
155 }
156}
157
158QPersistentModelIndex FitScriptGeneratorDataTable::hoveredRowIndex(QEvent *event) {
160 auto const eventType = event->type();
161 if (eventType == QEvent::HoverMove)
162 index = QPersistentModelIndex(this->indexAt(static_cast<QHoverEvent *>(event)->pos()));
163 else if (eventType == QEvent::Leave)
164 index = QPersistentModelIndex(QModelIndex());
165
166 return index;
167}
168
170 return getText(row, ColumnIndex::WorkspaceName).toStdString();
171}
172
174 return getText(row, ColumnIndex::WorkspaceIndex).toInt();
175}
176
178 return getText(row, ColumnIndex::StartX).toDouble();
179}
180
182 return getText(row, ColumnIndex::EndX).toDouble();
183}
184
185std::vector<FitDomainIndex> FitScriptGeneratorDataTable::allRows() const {
186 std::vector<FitDomainIndex> rowIndices;
187 rowIndices.reserve(this->rowCount());
188 for (auto index = 0; index < this->rowCount(); ++index)
189 rowIndices.emplace_back(FitDomainIndex(index));
190
191 std::reverse(rowIndices.begin(), rowIndices.end());
192 return rowIndices;
193}
194
195std::vector<FitDomainIndex> FitScriptGeneratorDataTable::selectedRows() const {
196 std::vector<FitDomainIndex> rowIndices;
197
198 auto const selectionModel = this->selectionModel();
199 if (selectionModel->hasSelection()) {
200 const auto rows = selectionModel->selectedRows();
201 std::transform(rows.cbegin(), rows.cend(), std::back_inserter(rowIndices),
202 [](const auto &rowIndex) { return FitDomainIndex(static_cast<std::size_t>(rowIndex.row())); });
203 std::reverse(rowIndices.begin(), rowIndices.end());
204 }
205 return rowIndices;
206}
207
209 if (hasLoadedData()) {
210 auto const rows = selectedRows();
211 return rows.size() > 0 ? rows[0] : FitDomainIndex{0};
212 }
213
214 throw std::runtime_error("There is no currentRow as data has not been loaded yet.");
215}
216
217bool FitScriptGeneratorDataTable::hasLoadedData() const { return this->rowCount() > 0; }
218
220 auto const rows = selectedRows();
221 if (rows.empty())
222 return "";
223 return this->verticalHeaderItem(static_cast<int>(rows[0].value))->text();
224}
225
226void FitScriptGeneratorDataTable::renameWorkspace(QString const &workspaceName, QString const &newName) {
227 for (auto rowIndex = 0; rowIndex < this->rowCount(); ++rowIndex) {
228 auto tableItem = this->item(rowIndex, ColumnIndex::WorkspaceName);
229 if (tableItem->text() == workspaceName)
230 tableItem->setText(newName);
231 }
232}
233
235 this->removeRow(static_cast<int>(domainIndex.value));
237
239
240 if (m_selectedRows.empty() && this->rowCount() > 0)
241 this->selectRow(0);
242
244}
245
246void FitScriptGeneratorDataTable::addDomain(QString const &workspaceName, MantidWidgets::WorkspaceIndex workspaceIndex,
247 double startX, double endX) {
248 this->blockSignals(true);
249
250 auto const rowIndex = this->rowCount();
251 this->insertRow(rowIndex);
252
253 this->setVerticalHeaderItem(rowIndex, createTableItem(toFunctionIndex(FitDomainIndex(rowIndex)), Qt::AlignCenter,
254 false, FUNCTION_INDEX_COLOR));
255 this->setItem(rowIndex, ColumnIndex::WorkspaceName, createTableItem(workspaceName, Qt::AlignVCenter, false));
256 this->setItem(rowIndex, ColumnIndex::WorkspaceIndex,
257 createWSIndexTableItem(static_cast<int>(workspaceIndex.value), Qt::AlignCenter, false));
258 this->setItem(rowIndex, ColumnIndex::StartX, createXTableItem(startX, Qt::AlignCenter, true));
259 this->setItem(rowIndex, ColumnIndex::EndX, createXTableItem(endX, Qt::AlignCenter, true));
260
261 if (!this->selectionModel()->hasSelection()) {
262 m_selectedRows.emplace_back(rowIndex);
263 this->selectRow(rowIndex);
264 }
265
266 this->blockSignals(false);
267}
268
270 for (auto i = FitDomainIndex(0); i < FitDomainIndex(this->rowCount()); ++i)
271 this->setVerticalHeaderItem(static_cast<int>(i.value),
272 createTableItem(toFunctionIndex(i), Qt::AlignCenter, false, FUNCTION_INDEX_COLOR));
273}
274
276 return this->item(static_cast<int>(row.value), column)->text();
277}
278
280 if (!m_selectedRows.empty() && m_selectedColumn >= 0)
281 setSelectedXValue(this->item(static_cast<int>(m_selectedRows[0].value), m_selectedColumn)->text().toDouble());
282}
283
285
287 this->verticalHeader()->setVisible(visible);
288}
289
291 this->blockSignals(true);
292 if (!m_selectedRows.empty())
293 this->setItem(static_cast<int>(m_selectedRows[0].value), m_selectedColumn,
294 createXTableItem(xValue, Qt::AlignCenter, true));
295 this->blockSignals(false);
296}
297
303 : QStyledItemDelegate(parent), m_tableWidget(parent), m_columnIndex(index), m_hoveredIndex(-1) {
304 m_tableWidget = parent;
305 m_tableWidget->setMouseTracking(true);
306
307 connect(m_tableWidget, SIGNAL(itemEntered(QTableWidgetItem *)), this, SLOT(handleItemEntered(QTableWidgetItem *)));
308 connect(m_tableWidget, SIGNAL(itemExited(int)), this, SLOT(handleItemExited(int)));
309}
310
311void CustomItemDelegate::handleItemEntered(QTableWidgetItem *item) {
312 m_hoveredIndex = item->row();
313 m_tableWidget->viewport()->update();
314}
315
316void CustomItemDelegate::handleItemExited(int newRowIndex) { m_hoveredIndex = newRowIndex; }
317
318QWidget *CustomItemDelegate::createEditor(QWidget *parent, QStyleOptionViewItem const &option,
319 QModelIndex const &index) const {
320 Q_UNUSED(option);
321 Q_UNUSED(index);
322
323 auto lineEdit = new QLineEdit(parent);
324 switch (m_columnIndex) {
325 case ColumnIndex::WorkspaceName:
326 break;
327 case ColumnIndex::WorkspaceIndex:
328 lineEdit->setValidator(createWSIndexValidator());
329 break;
330 case ColumnIndex::StartX:
331 lineEdit->setValidator(createXValidator());
332 break;
333 case ColumnIndex::EndX:
334 lineEdit->setValidator(createXValidator());
335 break;
336 }
337
338 return lineEdit;
339}
340
341void CustomItemDelegate::paint(QPainter *painter, QStyleOptionViewItem const &option, QModelIndex const &index) const {
342 auto opt = QStyleOptionViewItem(option);
343 if (index.row() == m_hoveredIndex)
344 opt.state |= QStyle::State_MouseOver;
345 QStyledItemDelegate::paint(painter, opt, index);
346}
347
348} // namespace MantidQt::MantidWidgets
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
This class is used for formating the type of data allowed in each of the tables columns.
QWidget * createEditor(QWidget *parent, QStyleOptionViewItem const &option, QModelIndex const &index) const override
void paint(QPainter *painter, QStyleOptionViewItem const &option, QModelIndex const &index) const override
CustomItemDelegate(FitScriptGeneratorDataTable *parent, ColumnIndex const &index)
CustomItemDelegate class methods.
This class represents the table widget which holds domain data for the FitScriptGenerator interface.
void renameWorkspace(QString const &workspaceName, QString const &newName)
QString getText(FitDomainIndex row, int column) const
void addDomain(QString const &workspaceName, MantidWidgets::WorkspaceIndex workspaceIndex, double startX, double endX)
void removeDomain(MantidWidgets::FitDomainIndex domainIndex)
MantidWidgets::WorkspaceIndex workspaceIndex(FitDomainIndex row) const
bool eventFilter(QObject *widget, QEvent *event) override
FitScriptGeneratorDataTable(QWidget *parent=nullptr)
FitScriptGeneratorDataTable class methods.
IndexType< 0 > FitDomainIndex
Definition IndexTypes.h:59
A struct to impliment strongly typed integers, without implicit conversion.
Definition IndexTypes.h:24
IntImplementationType value
Definition IndexTypes.h:26