Selected Rows in QTableView, copy to QClipboard

后端 未结 12 545
别那么骄傲
别那么骄傲 2020-12-25 08:14

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

相关标签:
12条回答
  • 2020-12-25 08:46

    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

    0 讨论(0)
  • 2020-12-25 08:46

    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()));
    
    0 讨论(0)
  • 2020-12-25 08:47

    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!

    0 讨论(0)
  • 2020-12-25 08:49

    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);
    }
    
    0 讨论(0)
  • 2020-12-25 08:57

    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
    
    0 讨论(0)
  • 2020-12-25 08:59

    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.

    0 讨论(0)
提交回复
热议问题