Pregunta

Tengo una base de datos SQLite y la hice en un QSqlTableModel . Para mostrar la base de datos, puse ese modelo en un QTableView .

Ahora quiero crear un Método donde las Filas seleccionadas (o la Línea completa) se copiarán en el QClipboard . Después de eso, quiero insertarlo en mi OpenOffice.Calc-Document.

Pero no tengo idea de qué hacer con la SEÑAL Seleccionada y el QModelIndex y cómo poner esto en el Portapapeles.

¿Fue útil?

Solución

Para capturar la selección, utilice el modelo de selección de la vista del elemento para obtener una lista de índices . Dado que tiene un QTableView * llamado view , obtiene la selección de esta manera:

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

Luego recorra la lista de índice llamando a model- > data (index) en cada índice. Convierta los datos a una cadena si aún no lo está y concatene cada cadena. Luego puede usar QClipboard.setText para pegar el resultado en el portapapeles. Tenga en cuenta que, para Excel y Calc, cada columna está separada de la siguiente por una nueva línea (" \ n ") y cada fila está separada por una pestaña (" \ t "). Debe verificar los índices para determinar cuándo pasar a la siguiente fila.

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

Advertencia : no he tenido la oportunidad de probar este código, pero un equivalente de PyQt funciona.

Otros consejos

Tuve un problema similar y terminé adaptando QTableWidget (que es una extensión de QTableView) para agregar la funcionalidad de copiar / pegar. Aquí está el código que se basa en lo proporcionado por quark arriba:

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 respuesta de Quark (la seleccionada) es buena para señalar a las personas en la dirección correcta, pero su algoritmo es completamente incorrecto. Además de un error desactivado por uno y una asignación incorrecta, ni siquiera es sintácticamente correcto. A continuación se muestra una versión funcional que acabo de escribir y probar.

Supongamos que nuestra tabla de ejemplo se ve así:

A | B | C
D | E | F

El problema con el algoritmo de Quark es el siguiente:

Si reemplazamos su separador \ t con un '| ', producirá esta salida:
B | C | D
E | F |

El error uno por uno es que D aparece en la primera fila. La asignación incorrecta se evidencia por la omisión de A

El siguiente algoritmo corrige estos dos problemas con la sintaxis correcta.

    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 razón por la que elegí usar un contador en lugar de un iterador es simplemente porque es más fácil probar si existe otro índice al compararlo con el conteo. Con un iterador, supongo que tal vez podrías simplemente incrementarlo y almacenarlo en un puntero débil para probar si es válido, pero solo usar un contador como lo hice anteriormente.

Necesitamos verificar si la línea siguiente estará en una nueva fila. Si estamos en una nueva fila y verificamos la fila anterior como lo hace el algoritmo de Quark, ya es demasiado tarde para agregar. Podríamos anteponer, pero luego tenemos que hacer un seguimiento del último tamaño de cadena. El código anterior producirá el siguiente resultado de la tabla de ejemplo:

A | B | C
D | E | F

Por alguna razón no tuve acceso a la función std :: sort, sin embargo, encontré que, como una buena alternativa a la solución de Corwin Joy, la función de clasificación se puede implementar reemplazando

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

con

  qSort(indexes);

Esto es lo mismo que escribir:

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

¡Gracias por su útil código, chicos!

Lo que deberá hacer es acceder a los datos de texto en el modelo, luego pasar ese texto a QClipboard .

Para acceder a los datos de texto en el modelo, use QModelIndex :: data () . El argumento predeterminado es Qt :: DisplayRole , es decir, el texto que se muestra.

Una vez que haya recuperado el texto, páselo al portapapeles con QClipboard :: setText () .

un ejemplo de 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))

Escribí un código basado en algunas de las respuestas de los demás. Subclasifiqué QTableWidget y anulé keyPressEvent () para permitir al usuario copiar las filas seleccionadas al portapapeles escribiendo 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);
    }
}

Ejemplo de salida (separado por tabulaciones):

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

Finalmente lo tengo, gracias.

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

y

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

No puedo evitar notar que puede simplificar su código usando un foreach () construcción y la QStringList , que tiene una práctica join () función.

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

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

Cuidado con el último elemento. Nota a continuación, los índices pueden quedar vacíos después de 'removeFirst ()'. Por lo tanto, 'actual' nunca es válido y no debe usarse en model () - > data (current).

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

Considera

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

Aquí hay una variación de lo que Corwin Joy publicó que funciona con QTableView y maneja las selecciones dispersas de manera diferente. Con este código, si tiene diferentes columnas seleccionadas en diferentes filas (por ejemplo, las celdas seleccionadas son (1,1), (1, 2), (2, 1), (3,2)), luego, cuando lo pegue, quedará vacío. celdas correspondientes a los agujeros en su selección (por ejemplo, celdas (2,2) y (3,1)). También muestra el texto del encabezado de la columna para las columnas que se cruzan con la selección.

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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top