Pergunta

Eu tenho um SQLite base de dados e eu fiz isso em um QSqlTableModel. Para mostrar o banco de dados, eu coloquei que o modelo em um QTableView.

Agora eu quero criar um método em que as linhas selecionadas (ou toda a linha) será copiado para o QClipboard. Depois que eu quero para inseri-lo no meu OpenOffice.Calc-documento.

Mas eu não tenho idéia do que fazer com o Selected sinal eo QModelIndex e como colocar isso em área de transferência.

Foi útil?

Solução

Para realmente capturar a seleção que você use modelo de seleção da tela do item para obter uma href="https://doc.qt.io/qt-5/qmodelindex.html#QModelIndexList-typedef" lista de índices . Dado que você tem um QTableView * chamado view você começa a seleção desta maneira:

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

circuito Em seguida, através da listagem de índices chamando model->data(index) em cada índice. Converter os dados em uma string, se não é já e concatenar cada corda juntos. Então você pode usar QClipboard.setText para colar o resultado para a área de transferência. Note-se que, para o Excel e Calc, cada coluna é separada da seguinte por uma nova linha ( "\ n") e cada linha é separado por um separador ( "\ t"). Você tem que verificar os índices para determinar quando você se move para a próxima linha.

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

Aviso :. Eu não tive a oportunidade de experimentar este código, mas um PyQt obras equivalentes

Outras dicas

Eu tive um problema semelhante e acabou adaptando QTableWidget (que é uma extensão do QTableView) para adicionar copiar / colar funcionalidade. Aqui está o código que constrói sobre o que foi fornecido por quark acima:

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

}

A resposta de Quark (o selecionado) é bom para apontar as pessoas na direção certa, mas seu algoritmo é totalmente incorreto. Além de fora por um erro e atribuição incorreta, não é mesmo sintaticamente correto. Abaixo está uma versão de trabalho que eu escrevi e testado.

Vamos supor que nossos olhares exemplo tabela assim:

A | B | C
D | E | F

O problema com o algoritmo do Quark é o seguinte:

Se substituirmos o \ t separador com um ' | ', que irá produzir esta saída:
B | C | D
E | F |

O off por um erro é que D aparece na primeira linha. A atribuição incorreta é evidenciado pela omissão de A

O algoritmo seguinte corrige estes dois problemas com sintaxe correta.

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

A razão que eu escolhi para usar um contador em vez de um iterador é apenas porque é mais fácil para testar se existe outro índice, verificando contra a contagem. Com um iterador, suponho que talvez você poderia apenas incrementá-lo e armazená-lo em um ponteiro fraco para testar se ele é válido, mas é só usar um contador, como eu fiz acima.

Precisamos verificar se o próxima linha estará em em uma nova linha. Se estamos em uma nova linha e vamos verificar a linha anterior como o algoritmo do Quark faz, a sua já muito tarde para acrescentar. Poderíamos acrescentar antes, mas então temos de acompanhar o último tamanho string. O código acima irá produzir a seguinte saída da tabela de exemplo:

A | B | C
D | E | F

Por alguma razão eu não tinha acesso à função std :: sort, no entanto eu achei que, como uma alternativa pura à solução de Corwin Alegria, a função de ordenação pode ser implementado, substituindo

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

com

  qSort(indexes);

Este é o mesmo que escrever:

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

Obrigado por seus rapazes código votos!

O que você precisa fazer é acessar os dados de texto no modelo, em seguida, passar esse texto para o QClipboard .

Para acessar os dados de texto no modelo, use QModelIndex::data() . O argumento padrão é Qt::DisplayRole, ou seja, o texto exibido.

Uma vez que você pegou o texto, passar esse texto para a área de transferência usando QClipboard::setText() .

a PyQt py2.x exemplo:

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

Eu escrevi alguns códigos com base em algumas das respostas dos outros. I subclasse QTableWidget e keyPressEvent() cancelou para permitir que o usuário copiar as linhas selecionadas para a área de transferência, 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);
    }
}

exemplo de saída (guia-separadas):

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

Eu finalmente consegui-lo, obrigado.

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

Eu não posso deixar de notar que você pode simplificar o seu código usando um foreach() construção ea href="http://doc.qtsoftware.com/4.5/qstringlist.html" rel="nofollow noreferrer"> QStringList classe , que tem um conveniente join() função.

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

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

Cuidado com o último elemento. Nota abaixo, índices poderá ficar vazio após 'removeFirst ()'. Assim, 'atual' nunca é válido e não deve ser usado no modelo (.) -> dados (atual)

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

Considere

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

Aqui é uma variação sobre o que Corwin Joy postou que funciona com QTableView e alças esparsas seleções de forma diferente. Com este código se você tem diferentes colunas selecionadas em linhas diferentes (por exemplo, células selecionadas são (1,1), (1, 2), (2, 1), (3,2)), então quando você colá-lo você vai ficar vazia células correspondentes para os "buracos" no seu selecção (por exemplo, células (2,2) e (3,1)). Ele também puxa no texto do cabeçalho da coluna para colunas que cruzam a seleção.

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);
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top