Tab to next visible column in QTableView - c++

I have a custom QTableView and QAbstractTableModel. My QTableView hides some of the columns from the QAbstractTableModel as they aren't needed.
When I hit Tab, I would like to select the next available (editable) column. My current implementation is to grab the next index from the QAbstractTableModel, but this index includes columns that are hidden. (So when hitting Tab it may be a couple presses before you see the "next" column selected.)
How can I tell Tab to jump to the next visible column?
The language is C++. Below is the code within my QTableView:
void keyPressEvent(QKeyEvent* event)
{
if((event->modifiers() == Qt::KeyboardModifier::NoModifier) && (event->key() == Qt::Key::Key_Tab))
{
this->moveToNextCell();
}
else
{
this->QTableView::keyPressEvent(event);
}
}
void moveToNextCell()
{
const QModelIndex index = this->currentIndex();
int nextColumn = index.column() + 1;
if(index.column() <= lastEditableCol)
{
this->setCurrentIndex(model->index(index.row(), nextColumn));
}
}

It's not elegant, but I've solved the problem by using isColumnHidden() from QTableView. I just iterate through the columns until I find one that isn't hidden.
for(int i = nextColumn; i <= numOfColumns && nextColumn <= numOfColumns; i++)
{
if(this->isColumnHidden(nextColumn) == true)
{
nextColumn += 1;
}
else
{
i = numOfCol + 1;
}
}

Related

Always show a QComboBox in a cell of a QTableView

I have a QTableView with an associated model. I want to have a QComboBox in each cell of the third column.
I used a QItemDelegate as shown in this page : https://wiki.qt.io/Combo_Boxes_in_Item_Views.
It works but the combo box is only shown after double clicking in the cell, the user has to click again to show the list of possible values. This is a bit inconvenient.
Is there a way to make the combo boxes always visible ?
I'm trying with openPersistentEditor method, but it does not work right now ...
there is a bit of code close to my code (ComboBoxItemDelegate is the same than the exemple linked before):
MonWidget::MonWidget() : _ui(new Ui::MonWidget())
{
// ...
// Links Model
_linksModel = new QStandardItemModel(this);
// Links Headers
QStringList linksTableViewHeader << "Name" << "Path" << "Version";
_linksModel->setHorizontalHeaderLabels(linksTableViewHeader) ;
// Create itemDelegate for linksView
_itemDelegate = new ComboBoxItemDelegate(_ui->_linksView);
// Set the links model on the links table view
_ui->_linksView->setModel(_linksModel);
_ui->_linksView->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
_ui->_linksView->horizontalHeader()->setStretchLastSection(true);
_ui->_linksView->horizontalHeader()->setMinimumSectionSize(100);
_ui->_linksView->setSelectionBehavior(QAbstractItemView::SelectRows);
_ui->_linksView->setItemDelegate(_itemDelegate);
}
// AddLinksInListView : method called when I add some rows ...
void MonWidget::AddLinksInListView(QList<DataItem*> listLinks)
{
int j=0;
int initialLinksNumber = _linksModel->rowCount();
// For each link, add a row with the link information
for (int row=initialLinksNumber; row<(initialLinksNumber + listLinks.size()) ; row++)
{
for (int column = 0; column < _linksModel->columnCount(); column++) {
//item used to display information for each column in the table view of links contained in the collection
QStandardItem *item=0;
switch(column)
{
case MonWidget::NAME:
{
item = new QStandardItem(listLinks.at(j)->data(MonWidget::NAME).toString());
_linksModel->setItem(row, column, item);
break;
}
case MonWidget::PATH:
{
item = new QStandardItem(listLinks.at(j)->data(MonWidget::PATH).toString());
_linksModel->setItem(row, column, item);
break;
}
case MonWidget::VERSION:
{
item = new QStandardItem(listLinks.at(j)->data(MonWidget::PATH).toString());
_linksModel->setItem(row, column, item);
_ui->_linksView->openPersistentEditor(_linksModel->index(row, column));
break;
}
}
}
j++;
}
}
Yes, there is a way, using openPersistentEditor. Assuming your model is named myModel:
YourDelegate* comboDelegate = new YourDelegate(this);
setItemDelegateForColumn(2, comboDelegate); // delegate set for the third column
...
// when adding a new line, use this.
// The combo box will be always visible
openPersistentEditor(myModel->index(iRow, 1));

showing a lot of widgets in qt5 is slow

I'm developing an program that allows you to communicate with an Engine control unit of a small turbine. The ECU is configured using 255 8-bit numbers stored in an EEPROM (ECU communicates over RS232). each number represents one of the engine's settings.
So to make sense of this we have a file that describes the meaning of all these settings and this is loaded into my program, which generates a row of 15 standard QWidgets per setting.
I also implemented a way to categorize these settings, for example, all fuel consumption related settings belong to one category. there's a Qcombobox in which you can select which category you want to filter. clicking it calls the following function:
void MainWindow::on_listCategories_currentTextChanged(const QString &currentcategory) { //filter categories
//ui->checkBoxShowDifferences->setCheckState(Qt::Unchecked); //ugly hack!!
setUpdatesEnabled(false);
if(currentcategory == "ALL") {
for(int i=0; i<RegistrySettings.size(); i++) {
if(RegistrySettings[i].Policy < static_cast<int>(TMC_Program.CurrentActiveAccessPolicy)) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
if(ui->checkBoxShowDifferences->isChecked()) {
if(RegistrySettings[i].value == RegistrySettings[i].compareValue) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
}
showOptionWidgets(RegistrySettings[i].address);
}
} else {
for(int i=0; i<RegistrySettings.size(); i++) {
if(RegistrySettings[i].Policy < static_cast<int>(TMC_Program.CurrentActiveAccessPolicy)) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
if(RegistrySettings[i].category != currentcategory) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
if(ui->checkBoxShowDifferences->isChecked()) {
if(RegistrySettings[i].value == RegistrySettings[i].compareValue) {
hideOptionWidgets(RegistrySettings[i].address);
continue;
}
showOptionWidgets(RegistrySettings[i].address);
} else {
hideOptionWidgets(RegistrySettings[i].address);
}
}
}
setUpdatesEnabled(true);
while hiding works perfectly fine, when selecting "ALL" it needs to show all the hidden widgets, this is incredibly slow! it takes around 5 seconds to display all the widgets!
The code responsible for showing all the widgets is this:
void MainWindow::showOptionWidgets(int addr) {
if(addr < 0 || addr > 255) {return;}
QStringList WidgetNames = generateOptionRowWidgetNames(addr);
for(int i=1; i<WidgetNames.size(); i++) { //number 0 is a qhboxlayout and not a qwidget so it won't find it
QWidget* widgetToShow = ui->scrollAreaOptions->findChild<QWidget*>(WidgetNames[i]);
if(widgetToShow && widgetToShow->isHidden()) {
widgetToShow->show(); //extremely slow!
}
}
}
is there a way to optimize this?
note: all widgets are standard ones! just labels, checkboxes and buttons, generate optionRowWidgetNames only generates a simple QStringlist with fixed strings that only have the corrosponding settings number attached to them.

How to get current row of QTableWidget if I clicked on its child?

I have created a QTableWidget in which I've used setCellWidget(QWidget*). I've set QLineEdit in the cell widget. I've also created a delete button and clicking that button sends a signal to the function deleteRow. I've also used a function currentRow() to get the current row, but it returns -1 because of the QLineEdit. The code snippet is below.
void createTable() {
m_table = new QTableWidget(QDialog); //member variable
for (int i = 0; i < 3; i++)
{
QLineEdit *lineEdit = new QLineEdit(m_table);
m_table->setCellWidget(i, 0, lineEdit);
}
QPushButton *deleteBut = new QPushButton(QDiaolg);
connect(deleteBut, SIGNAL(clicked()), QDialog, SLOT(editRow()));
}
editRow() {
int row = m_table->currentRow(); // This gives -1
m_table->remove(row);
}
In above scenario I click in the QLineEdit and then click on the button delete. Please help me out with a solution.
Just tried it here, it seems that currentRow of the table returns -1 when clicking the button right after program start, and when first selecting a cell, then selecting the QLineEdit and then clicking the button, the correct row is returned.
I would do the following as a workaround: Save the row number in the QLineEdit, e.g. by using QObject::setProperty:
QLineEdit *lineEdit = new QLineEdit(m_table);
lineEdit->setProperty("row", i);
m_table->setCellWidget(i, 0, lineEdit);
Then, in the editRow handler, retrieve the property by asking the QTableWidget for its focused child:
int row = m_table->currentRow();
if (row == -1) {
if (QWidget* focused = m_table->focusWidget()) {
row = focused->property("row").toInt();
}
}
The accepted solution, as is, would not work if rows might get deleted while the program runs. Thus the approach would require to update all the properties. Can be done, if this is a rare operation.
I got away with an iteration approach:
for(unsigned int i = 0; i < table->rowCount(); ++i)
{
if(table->cellWidget(i, relevantColumn) == QObject::sender())
{
return i;
}
}
return -1;
Quick, dirty, but worked, and in my case more suitable, as rows got deleted often or changed their positions, only buttons in the widget were connected to the slot and the slot was never called directly. If these conditions are not met, further checks might get necessary (if(QObject::sender()) { /* */ }, ...).
Karsten's answer will work correctly only if QLineEdit's property is recalculated each time a row is deleted, which might be a lot of work. And Aconcagua's answer works only if the method is invoked via signal/slot mechanism. In my solution, I just calculate the position of the QlineEdit which has focus (assuming all table items were set with setCellWidget):
int getCurrentRow() {
for (int i=0; i<myTable->rowCount(); i++)
for (int j=0; j<myTable->columnCount(); j++) {
if (myTable->cellWidget(i,j) == myTable->focusWidget()) {
return i;
}
}
return -1;
}

Parent-dependent QTreeWidgetItem checkboxes in dynamically generated QTreeWidget

I'm writing an application that has a QTreeWidget that is populated by parsing an XML file containing the tree levels. If I select a top level checkbox, I need all of the sub-level checkboxes to be checked also.
I already have the XML parser working and populating the QTreeWidget with QTreeWidgetItems that have checkboxes but they can only be individually checked.
To do this, keep the code you have to generate the tree with your XML. Then connect to the itemChanged() signal and update the check states in a slot. It should look something like:
connect(treeWidget, SIGNAL(itemChanged(QTreeWidgetItem*, int)),
this, SLOT(updateChecks(QTreeWidgetItem*, int)));
void ClassName::updateChecks(QTreewidgetItem* item, int column)
{
// Checkstate is stored on column 0
if(column != 0)
return;
recursiveChecks(item);
}
void ClassName::recursiveChecks(QTreeWidgetItem* parent)
{
Qt::CheckState checkState = parent->checkState(0);
for(int i = 0; i < parent->childCount(); ++i)
{
parent->child(i)->setCheckState(0, checkState);
recursiveChecks(parent->child(i));
}
}
A few notes to consider:
You may be tempted to use the itemClicked signal instead of the itemChanged signal. This usually works, but will not work when the user uses the arrow keys and the space bar to change checkstates.
You will need to think about what will happen when you uncheck one of the sub-items that have been checked by clicking on its parent. Usually this means you need to make all ancestors either uncheck or partially-checked. This may not be true for your case.
itemUpdated will also get fired for other changes to the item (like the text changing), so be aware that this is not a super efficient way of doing this.
I just worked on this a little and got nice results based on Rick's answer. Maybe it can help other out there.
It updates the state of parents and children with a tristate status for parents only (checked, unchecked, partially-checked).
void ClassName::updateChecks(QTreeWidgetItem *item, int column)
{
bool diff = false;
if(column != 0 && column!=-1)
return;
if(item->childCount()!=0 && item->checkState(0)!=Qt::PartiallyChecked && column!=-1){
Qt::CheckState checkState = item->checkState(0);
for (int i = 0; i < item->childCount(); ++i) {
item->child(i)->setCheckState(0, checkState);
}
} else if (item->childCount()==0 || column==-1) {
if(item->parent()==0)
return;
for (int j = 0; j < item->parent()->childCount(); ++j) {
if(j != item->parent()->indexOfChild(item) && item->checkState(0)!=item->parent()->child(j)->checkState(0)){
diff = true;
}
}
if(diff)
item->parent()->setCheckState(0,Qt::PartiallyChecked);
else
item->parent()->setCheckState(0,item->checkState(0));
if(item->parent()!=0)
updateChecks(item->parent(),-1);
}
}
Doesn't need recursiveChecks() anymore. Connect between the treeWidget and updateChecks still active.
This appears still quite high in search engines, and is outdated.
Just set the flag Qt::ItemIsAutoTristate on your top-level item.

Selected Rows in QTableView, copy to QClipboard

I have a SQLite-Database and I did it into a QSqlTableModel.
To show the Database, I put that Model into a QTableView.
Now I want to create a Method where the selected Rows (or the whole Line) will be copied into the QClipboard. After that I want to insert it into my OpenOffice.Calc-Document.
But I have no Idea what to do with the Selected SIGNAL and the QModelIndex and how to put this into the Clipboard.
To actually capture the selection you use the item view's selection model to get a list of indices. Given that you have a QTableView * called view you get the selection this way:
QAbstractItemModel * model = view->model();
QItemSelectionModel * selection = view->selectionModel();
QModelIndexList indexes = selection->selectedIndexes();
Then loop through the index list calling model->data(index) on each index. Convert the data to a string if it isn't already and concatenate each string together. Then you can use QClipboard.setText to paste the result to the clipboard. Note that, for Excel and Calc, each column is separated from the next by a newline ("\n") and each row is separated by a tab ("\t"). You have to check the indices to determine when you move to the next row.
QString selected_text;
// You need a pair of indexes to find the row changes
QModelIndex previous = indexes.first();
indexes.removeFirst();
foreach(const QModelIndex &current, indexes)
{
QVariant data = model->data(current);
QString text = data.toString();
// At this point `text` contains the text in one cell
selected_text.append(text);
// If you are at the start of the row the row number of the previous index
// isn't the same. Text is followed by a row separator, which is a newline.
if (current.row() != previous.row())
{
selected_text.append('\n');
}
// Otherwise it's the same row, so append a column separator, which is a tab.
else
{
selected_text.append('\t');
}
previous = current;
}
QApplication.clipboard().setText(selected_text);
Warning: I have not had a chance to try this code, but a PyQt equivalent works.
I had a similar problem and ended up adapting QTableWidget (which is an extension of QTableView) to add copy/paste functionality. Here is the code which builds on what was provided by quark above:
qtablewidgetwithcopypaste.h
// QTableWidget with support for copy and paste added
// Here copy and paste can copy/paste the entire grid of cells
#ifndef QTABLEWIDGETWITHCOPYPASTE_H
#define QTABLEWIDGETWITHCOPYPASTE_H
#include <QTableWidget>
#include <QKeyEvent>
#include <QWidget>
class QTableWidgetWithCopyPaste : public QTableWidget
{
Q_OBJECT
public:
QTableWidgetWithCopyPaste(int rows, int columns, QWidget *parent = 0) :
QTableWidget(rows, columns, parent)
{}
QTableWidgetWithCopyPaste(QWidget *parent = 0) :
QTableWidget(parent)
{}
private:
void copy();
void paste();
public slots:
void keyPressEvent(QKeyEvent * event);
};
#endif // QTABLEWIDGETWITHCOPYPASTE_H
qtablewidgetwithcopypaste.cpp
#include "qtablewidgetwithcopypaste.h"
#include <QApplication>
#include <QMessageBox>
#include <QClipboard>
#include <QMimeData>
void QTableWidgetWithCopyPaste::copy()
{
QItemSelectionModel * selection = selectionModel();
QModelIndexList indexes = selection->selectedIndexes();
if(indexes.size() < 1)
return;
// QModelIndex::operator < sorts first by row, then by column.
// this is what we need
// std::sort(indexes.begin(), indexes.end());
qSort(indexes);
// You need a pair of indexes to find the row changes
QModelIndex previous = indexes.first();
indexes.removeFirst();
QString selected_text_as_html;
QString selected_text;
selected_text_as_html.prepend("<html><style>br{mso-data-placement:same-cell;}</style><table><tr><td>");
QModelIndex current;
Q_FOREACH(current, indexes)
{
QVariant data = model()->data(previous);
QString text = data.toString();
selected_text.append(text);
text.replace("\n","<br>");
// At this point `text` contains the text in one cell
selected_text_as_html.append(text);
// If you are at the start of the row the row number of the previous index
// isn't the same. Text is followed by a row separator, which is a newline.
if (current.row() != previous.row())
{
selected_text_as_html.append("</td></tr><tr><td>");
selected_text.append(QLatin1Char('\n'));
}
// Otherwise it's the same row, so append a column separator, which is a tab.
else
{
selected_text_as_html.append("</td><td>");
selected_text.append(QLatin1Char('\t'));
}
previous = current;
}
// add last element
selected_text_as_html.append(model()->data(current).toString());
selected_text.append(model()->data(current).toString());
selected_text_as_html.append("</td></tr>");
QMimeData * md = new QMimeData;
md->setHtml(selected_text_as_html);
// qApp->clipboard()->setText(selected_text);
md->setText(selected_text);
qApp->clipboard()->setMimeData(md);
// selected_text.append(QLatin1Char('\n'));
// qApp->clipboard()->setText(selected_text);
}
void QTableWidgetWithCopyPaste::paste()
{
if(qApp->clipboard()->mimeData()->hasHtml())
{
// TODO, parse the html data
}
else
{
QString selected_text = qApp->clipboard()->text();
QStringList cells = selected_text.split(QRegExp(QLatin1String("\\n|\\t")));
while(!cells.empty() && cells.back().size() == 0)
{
cells.pop_back(); // strip empty trailing tokens
}
int rows = selected_text.count(QLatin1Char('\n'));
int cols = cells.size() / rows;
if(cells.size() % rows != 0)
{
// error, uneven number of columns, probably bad data
QMessageBox::critical(this, tr("Error"),
tr("Invalid clipboard data, unable to perform paste operation."));
return;
}
if(cols != columnCount())
{
// error, clipboard does not match current number of columns
QMessageBox::critical(this, tr("Error"),
tr("Invalid clipboard data, incorrect number of columns."));
return;
}
// don't clear the grid, we want to keep any existing headers
setRowCount(rows);
// setColumnCount(cols);
int cell = 0;
for(int row=0; row < rows; ++row)
{
for(int col=0; col < cols; ++col, ++cell)
{
QTableWidgetItem *newItem = new QTableWidgetItem(cells[cell]);
setItem(row, col, newItem);
}
}
}
}
void QTableWidgetWithCopyPaste::keyPressEvent(QKeyEvent * event)
{
if(event->matches(QKeySequence::Copy) )
{
copy();
}
else if(event->matches(QKeySequence::Paste) )
{
paste();
}
else
{
QTableWidget::keyPressEvent(event);
}
}
Quark's answer (the selected one) is good for pointing people in the right direction, but his algorithm is entirely incorrect. In addition to an off by one error and incorrect assignment, its not even syntactically correct. Below is a working version that I just wrote and tested.
Let's assume our example table looks like so:
A | B | C
D | E | F
The problem with Quark's algorithm is the following:
If we replace his \t separator with a ' | ', it will produce this output:
B | C | D
E | F |
The off by one error is that D appears in the first row. The incorrect assignment is evidenced by the omission of A
The following algorithm corrects these two problems with correct syntax.
QString clipboardString;
QModelIndexList selectedIndexes = view->selectionModel()->selectedIndexes();
for (int i = 0; i < selectedIndexes.count(); ++i)
{
QModelIndex current = selectedIndexes[i];
QString displayText = current.data(Qt::DisplayRole).toString();
// If there exists another column beyond this one.
if (i + 1 < selectedIndexes.count())
{
QModelIndex next = selectedIndexes[i+1];
// If the column is on different row, the clipboard should take note.
if (next.row() != current.row())
{
displayText.append("\n");
}
else
{
// Otherwise append a column separator.
displayText.append(" | ");
}
}
clipboardString.append(displayText);
}
QApplication::clipboard()->setText(clipboardString);
The reason I chose to use a counter instead of an iterator is just because it is easier to test if there exists another index by checking against the count. With an iterator, I suppose maybe you could just increment it and store it in a weak pointer to test if it is valid but just use a counter like I did above.
We need to check if the next line will be on on a new row. If we are on a new row and we check the previous row as Quark's algorithm does, its already too late to append. We could prepend, but then we have to keep track of the last string size. The above code will produce the following output from the example table:
A | B | C
D | E | F
For whatever reason I didn't have access to the std::sort function, however I did find that as a neat alternative to Corwin Joy's solution, the sort function can be implemented by replacing
std::sort(indexes.begin(), indexes.end());
with
qSort(indexes);
This is the same as writing:
qSort(indexes.begin(), indexes.end());
Thanks for your helpful code guys!
I wrote some code based on some of the others' answers. I subclassed QTableWidget and overrode keyPressEvent() to allow the user to copy the selected rows to the clipboard by typing Control-C.
void MyTableWidget::keyPressEvent(QKeyEvent* event) {
// If Ctrl-C typed
if (event->key() == Qt::Key_C && (event->modifiers() & Qt::ControlModifier))
{
QModelIndexList cells = selectedIndexes();
qSort(cells); // Necessary, otherwise they are in column order
QString text;
int currentRow = 0; // To determine when to insert newlines
foreach (const QModelIndex& cell, cells) {
if (text.length() == 0) {
// First item
} else if (cell.row() != currentRow) {
// New row
text += '\n';
} else {
// Next cell
text += '\t';
}
currentRow = cell.row();
text += cell.data().toString();
}
QApplication::clipboard()->setText(text);
}
}
Output example (tab-separated):
foo bar baz qux
bar baz qux foo
baz qux foo bar
qux foo bar baz
What you'll need to do is access the text data in the model, then pass that text to the QClipboard.
To access the text data in the model, use QModelIndex::data(). The default argument is Qt::DisplayRole, i.e. the displayed text.
Once you've retrieved the text, pass that text to the clipboard using QClipboard::setText().
a pyqt py2.x example:
selection = self.table.selectionModel() #self.table = QAbstractItemView
indexes = selection.selectedIndexes()
columns = indexes[-1].column() - indexes[0].column() + 1
rows = len(indexes) / columns
textTable = [[""] * columns for i in xrange(rows)]
for i, index in enumerate(indexes):
textTable[i % rows][i / rows] = unicode(self.model.data(index).toString()) #self.model = QAbstractItemModel
return "\n".join(("\t".join(i) for i in textTable))
I finally got it, thanks.
void Widget::copy() {
QItemSelectionModel *selectionM = tableView->selectionModel();
QModelIndexList selectionL = selectionM->selectedIndexes();
selectionL.takeFirst(); // ID, not necessary
QString *selectionS = new QString(model->data(selectionL.takeFirst()).toString());
selectionS->append(", ");
selectionS->append(model->data(selectionL.takeFirst()).toString());
selectionS->append(", ");
selectionS->append(model->data(selectionL.takeFirst()).toString());
selectionS->append(", ");
selectionS->append(model->data(selectionL.takeFirst()).toString());
clipboard->setText(*selectionS);
}
and
connect (tableView, SIGNAL(clicked(QModelIndex)), this, SLOT(copy()));
I can't help but notice that you can simplify your code using a foreach() construct and the QStringList class, which has a convenient join() function.
void Widget::copy()
{
QStringList list ;
foreach ( const QModelIndex& index, tableView->selectedIndexes() )
{
list << index.data() ;
}
clipboard->setText( list.join( ", " ) ) ;
}
Careful with the last element. Note below, indexes may become empty after 'removeFirst()'. Thus, 'current' is never valid and should not be used in model()->data(current).
indexes.removeFirst();
QString selected_text;
QModelIndex current;
Q_FOREACH(current, indexes)
{
.
.
.
}
// add last element
selected_text.append(model()->data(current).toString());
Consider
QModelIndex last = indexes.last();
indexes.removeFirst();
QString selected_text;
Q_FOREACH(QModelIndex current, indexes)
{
.
.
.
}
// add last element
selected_text.append(model()->data(last).toString());
Here is a variation on what Corwin Joy posted that works with QTableView and handles sparse selections differently. With this code if you have different columns selected in different rows (e.g. selected cells are (1,1), (1, 2), (2, 1), (3,2)) then when you paste it you will get empty cells corresponding to the "holes" in your selection (e.g. cells (2,2) and (3,1)). It also pulls in the column header text for columns that intersect the selection.
void CopyableTableView::copy()
{
QItemSelectionModel *selection = selectionModel();
QModelIndexList indices = selection->selectedIndexes();
if(indices.isEmpty())
return;
QMap<int, bool> selectedColumnsMap;
foreach (QModelIndex current, indices) {
selectedColumnsMap[current.column()] = true;
}
QList<int> selectedColumns = selectedColumnsMap.uniqueKeys();
int minCol = selectedColumns.first();
// prepend headers for selected columns
QString selectedText;
foreach (int column, selectedColumns) {
selectedText += model()->headerData(column, Qt::Horizontal, Qt::DisplayRole).toString();
if (column != selectedColumns.last())
selectedText += QLatin1Char('\t');
}
selectedText += QLatin1Char('\n');
// QModelIndex::operator < sorts first by row, then by column.
// this is what we need
qSort(indices);
int lastRow = indices.first().row();
int lastColumn = minCol;
foreach (QModelIndex current, indices) {
if (current.row() != lastRow) {
selectedText += QLatin1Char('\n');
lastColumn = minCol;
lastRow = current.row();
}
if (current.column() != lastColumn) {
for (int i = 0; i < current.column() - lastColumn; ++i)
selectedText += QLatin1Char('\t');
lastColumn = current.column();
}
selectedText += model()->data(current).toString();
}
selectedText += QLatin1Char('\n');
QApplication::clipboard()->setText(selectedText);
}
If anybody is interested, this web page provide a working code project on this topic, it's working pretty well.
Copy / paste functionality implementation for QAbstractTableModel / QTableView