Domanda

Ho un database SQLite e l'ho fatto in un QSqlTableModel . Per mostrare il database, ho inserito quel modello in un QTableView .

Ora voglio creare un metodo in cui le righe selezionate (o l'intera riga) verranno copiate nel QClipboard . Dopodiché voglio inserirlo nel mio documento OpenOffice.Calc.

Ma non ho idea di cosa fare con il Selected SIGNAL e il QModelIndex e come metterlo negli Appunti.

È stato utile?

Soluzione

Per acquisire effettivamente la selezione, utilizzare il modello di selezione per ottenere un elenco di indici . Dato che hai un QTableView * chiamato view ottieni la selezione in questo modo:

QAbstractItemModel * model = view->model();
QItemSelectionModel * selection = view->selectionModel();
QModelIndexList indexes = selection->selectedIndexes();

Quindi scorrere l'elenco dell'indice chiamando modello- > data (indice) su ciascun indice. Converti i dati in una stringa se non lo è già e concatena ciascuna stringa insieme. Quindi puoi usare QClipboard.setText per incollare il risultato negli appunti. Nota che, per Excel e Calc, ogni colonna è separata dalla successiva da una nuova riga (" \ n ") e ogni riga è separata da una scheda (" \ t "). Devi controllare gli indici per determinare quando passi alla riga successiva.

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);

Avviso : non ho avuto la possibilità di provare questo codice, ma un equivalente PyQt funziona.

Altri suggerimenti

Ho avuto un problema simile e ho finito per adattare QTableWidget (che è un'estensione di QTableView) per aggiungere funzionalità copia / incolla. Ecco il codice che si basa su ciò che è stato fornito da Quark sopra:

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);
    }

}

La risposta di Quark (quella selezionata) è utile per indirizzare le persone nella giusta direzione, ma il suo algoritmo è del tutto errato. Oltre a un errore di un errore e un'assegnazione errata, non è nemmeno sintatticamente corretto. Di seguito è una versione funzionante che ho appena scritto e testato.

Supponiamo che la nostra tabella di esempio sia così:

A | B | C
D | E | F

Il problema con l'algoritmo di Quark è il seguente:

Se sostituiamo il suo \ t separatore con un '| ', produrrà questo output:
B | C | D
E | F |

L'errore di un errore è che D appare nella prima riga. L'assegnazione errata è evidenziata dall'omissione di A

Il seguente algoritmo corregge questi due problemi con la sintassi corretta.

    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);

Il motivo per cui ho scelto di utilizzare un contatore anziché un iteratore è solo perché è più semplice verificare se esiste un altro indice verificando il conteggio. Con un iteratore, suppongo che potresti semplicemente incrementarlo e memorizzarlo in un puntatore debole per testare se è valido ma usare un contatore come ho fatto sopra.

Dobbiamo verificare se la prossima linea sarà su una nuova riga. Se siamo su una nuova riga e controlliamo la riga precedente come fa l'algoritmo di Quark, è già troppo tardi per aggiungere. Potremmo anteporre, ma poi dobbiamo tenere traccia dell'ultima dimensione della stringa. Il codice precedente produrrà il seguente output dalla tabella di esempio:

A | B | C
D | E | F

Per qualsiasi motivo non ho avuto accesso alla funzione std :: sort, tuttavia ho scoperto che come alternativa ordinata alla soluzione di Corwin Joy, la funzione di ordinamento può essere implementata sostituendo

 std::sort(indexes.begin(), indexes.end());

con

  qSort(indexes);

È lo stesso della scrittura:

 qSort(indexes.begin(), indexes.end());

Grazie per il vostro utile codice ragazzi!

Quello che devi fare è accedere ai dati di testo nel modello, quindi passare quel testo a QClipboard .

Per accedere ai dati di testo nel modello, utilizzare QModelIndex :: data () . L'argomento predefinito è Qt :: DisplayRole , ovvero il testo visualizzato.

Dopo aver recuperato il testo, trasferiscilo negli Appunti utilizzando QClipboard :: setText () .

un esempio di pyqt py2.x:

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))

Ho scritto del codice basato su alcune delle risposte degli altri. Ho eseguito la sottoclasse di QTableWidget e ho ignorato keyPressEvent () per consentire all'utente di copiare negli appunti le righe selezionate digitando 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);
    }
}

Esempio di output (separato da tabulazioni):

foo bar baz qux
bar baz qux foo
baz qux foo bar
qux foo bar baz

Finalmente l'ho capito, grazie.

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);
}

e

connect (tableView, SIGNAL(clicked(QModelIndex)), this, SLOT(copy()));

Non posso fare a meno di notare che puoi semplificare il tuo codice utilizzando un foreach () e il QStringList , che ha una comoda join () funzione.

void Widget::copy()
{
   QStringList list ;
   foreach ( const QModelIndex& index, tableView->selectedIndexes() )
   {
      list << index.data() ;
   }

   clipboard->setText( list.join( ", " ) ) ;
}

Attento con l'ultimo elemento. Nota di seguito, gli indici potrebbero diventare vuoti dopo 'removeFirst ()'. Pertanto, "current" non è mai valido e non deve essere utilizzato nei dati model () - > (current).

  indexes.removeFirst();
  QString selected_text;
  QModelIndex current;
  Q_FOREACH(current, indexes)
  {
  .
  .
  .
  }
  // add last element
  selected_text.append(model()->data(current).toString());

Si consideri

  QModelIndex last = indexes.last();
  indexes.removeFirst();
  QString selected_text;
  Q_FOREACH(QModelIndex current, indexes)
  {
  .
  .
  .
  }
  // add last element
  selected_text.append(model()->data(last).toString());

Ecco una variante di ciò che Corwin Joy ha pubblicato che funziona con QTableView e gestisce le selezioni sparse in modo diverso. Con questo codice se hai colonne diverse selezionate in righe diverse (ad esempio, le celle selezionate sono (1,1), (1, 2), (2, 1), (3,2)) quindi quando lo incolli ti svuoterai celle corrispondenti ai "fori" nella tua selezione (ad es. celle (2,2) e (3,1)). Estrae anche il testo dell'intestazione di colonna per le colonne che intersecano la selezione.

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);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top