Question

J'ai une base de données SQLite et je l'ai fait dans un QSqlTableModel . Pour afficher la base de données, j'ai placé ce modèle dans un QTableView .

Je souhaite maintenant créer une méthode dans laquelle les lignes sélectionnées (ou la totalité de la ligne) seront copiées dans le QClipboard . Après cela, je veux l'insérer dans mon OpenOffice.Calc-Document.

Mais je ne sais pas quoi faire avec le Selected SIGNAL et le QModelIndex et comment le mettre dans le Presse-papiers.

Était-ce utile?

La solution

Pour capturer réellement la sélection, utilisez le modèle de sélection de la vue d'élément pour obtenir une liste d'indices . Etant donné que vous avez un QTableView * appelé view , vous obtenez la sélection de cette façon:

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

Parcourez ensuite la liste des index en appelant model- > data (index) sur chaque index. Convertissez les données en chaîne si ce n'est pas déjà fait et concaténez chaque chaîne. Ensuite, vous pouvez utiliser QClipboard.setText pour coller le résultat dans le Presse-papiers. Notez que pour Excel et Calc, chaque colonne est séparée de la suivante par une nouvelle ligne ("\ n") et chaque ligne est séparée par un onglet ("\ t"). Vous devez vérifier les index pour déterminer quand vous passez à la ligne suivante.

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

Avertissement : je n'ai pas eu l'occasion d'essayer ce code, mais un équivalent de PyQt fonctionne.

Autres conseils

J'ai eu un problème similaire et j'ai fini par adapter QTableWidget (qui est une extension de QTableView) pour ajouter une fonctionnalité copier / coller. Voici le code qui repose sur ce qui a été fourni par quark ci-dessus:

qtablewidgetwccopypaste.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

qtablewidgetwcopopaste.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 réponse de Quark (celle sélectionnée) permet de diriger les gens dans la bonne direction, mais son algorithme est tout à fait incorrect. Outre une erreur par une erreur et une affectation incorrecte, sa syntaxe n'est même pas correcte. Vous trouverez ci-dessous une version de travail que je viens d’écrire et de tester.

Supposons que notre exemple de table ressemble à ce qui suit:

A | B | C
D | E | F

Le problème de l'algorithme de Quark est le suivant:

Si nous remplaçons son séparateur \ t par un '| ', il générera cette sortie:
B | C | D
E | F |

L'erreur par une erreur est que D apparaît dans la première ligne. L'attribution incorrecte est mise en évidence par l'omission de A

.

L'algorithme suivant corrige ces deux problèmes avec une syntaxe correcte.

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

La raison pour laquelle j'ai choisi d'utiliser un compteur au lieu d'un itérateur est simplement parce qu'il est plus facile de tester s'il existe un autre index en vérifiant par rapport au compte. Avec un itérateur, je suppose que vous pourriez peut-être simplement l'incrémenter et le stocker dans un pointeur faible pour vérifier s'il est valide, mais utilisez simplement un compteur comme je l'ai fait ci-dessus.

Nous devons vérifier si la ligne suivante apparaît sur une nouvelle ligne. Si nous sommes sur une nouvelle ligne et que nous vérifions la ligne précédente comme le fait l'algorithme de Quark, il est déjà trop tard pour l'ajouter. Nous pourrions ajouter des éléments, mais nous devons ensuite garder trace de la dernière taille de chaîne. Le code ci-dessus générera la sortie suivante de la table d'exemple:

A | B | C
D | E | F

Pour une raison quelconque, je n’avais pas accès à la fonction std :: sort, mais j’ai trouvé qu’une alternative intéressante à la solution de Corwin Joy pouvait être implémentée en remplaçant

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

avec

  qSort(indexes);

Cela revient à écrire:

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

Merci pour votre code utile les gars!

Vous devez accéder aux données texte du modèle, puis le transmettre au QClipboard .

Pour accéder aux données texte du modèle, utilisez QModelIndex :: data () . L’argument par défaut est Qt :: DisplayRole , c’est-à-dire le texte affiché.

Une fois que vous avez récupéré le texte, transmettez-le au presse-papiers à l'aide de QClipboard :: setText () .

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

J'ai écrit du code basé sur les réponses des autres. J'ai sous-classé QTableWidget et redéfini keyPressEvent () pour permettre à l'utilisateur de copier les lignes sélectionnées dans le presse-papiers en tapant Ctrl-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);
    }
}

Exemple de sortie (séparé par des tabulations):

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

Je l'ai enfin obtenu, merci.

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

et

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

Je ne peux pas m'empêcher de remarquer que vous pouvez simplifier votre code à l'aide d'un foreach () et la structure QStringList classe, qui possède une join () fonction.

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

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

Attention au dernier élément. Remarque ci-dessous, les index peuvent devenir vides après 'removeFirst ()'. Ainsi, 'current' n'est jamais valide et ne doit pas être utilisé dans model () - > data (current).

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

Envisagez

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

Voici une variante de ce que Corwin Joy a publié qui fonctionne avec QTableView et gère les sélections clairsemées différemment. Avec ce code, si vous avez différentes colonnes sélectionnées dans différentes lignes (par exemple, les cellules sélectionnées sont (1,1), (1, 2), (2, 1), (3,2)), puis, lorsque vous les collez, elles seront vides. cellules correspondant aux "trous" dans votre sélection (par exemple, les cellules (2,2) et (3,1)). Il affiche également le texte de l'en-tête de colonne pour les colonnes qui intersectent la sélection.

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);
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top