Вопрос

У меня есть база данных SQLite, и я превратил ее в QSqlTableModel.Чтобы показать базу данных, я поместил эту модель в QTableView.

Теперь я хочу создать Метод, в котором выбранные строки (или вся строка целиком) будут скопированы в QClipboard.После этого я хочу вставить его в свой OpenOffice.Calc-документ.

Но я понятия не имею, что делать с этим Selected СИГНАЛ и QModelIndex и как поместить это в буфер обмена.

Это было полезно?

Решение

Чтобы фактически захватить выбор, вы используете модель выбора вида чтобы получить список индексов . Учитывая, что у вас есть QTableView * с именем view , вы получаете выбор следующим образом:

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

Затем переберите список индексов, вызывая model-> gt; data (index) для каждого индекса. Преобразуйте данные в строку, если это еще не сделано, и объедините каждую строку вместе. Затем вы можете использовать QClipboard.setText , чтобы вставить результат в буфер обмена. Обратите внимание, что для Excel и Calc каждый столбец отделен от следующего новой строкой (" \ n "), а каждая строка отделена вкладкой (" \ t "). Вы должны проверить индексы, чтобы определить, когда вы переходите к следующей строке.

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

Предупреждение . У меня не было возможности попробовать этот код, но эквивалент PyQt работает.

Другие советы

У меня была похожая проблема, и в итоге я адаптировал QTableWidget (который является расширением QTableView) для добавления функциональности копирования / вставки.Вот код, который основан на том, что было предоставлено quark выше:

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 | Б | C
D | E | F

Проблема с алгоритмом Кварка заключается в следующем:

Если мы заменим его \ t разделитель на '| , он выдаст такой вывод:
B | C | D
E | F |

Ошибка выключения в том, что D появляется в первой строке. Неправильное назначение подтверждается пропуском A

Следующий алгоритм исправляет эти две проблемы с правильным синтаксисом.

    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 | Б | C
D | E | F

По какой-то причине у меня не было доступа к функции std::sort, однако я обнаружил, что в качестве удобной альтернативы решению Corwin Joy функция сортировки может быть реализована путем замены

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

с

  qSort(indexes);

Это то же самое, что писать:

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

Спасибо за ваш полезный код, ребята!

Вам нужно получить доступ к текстовым данным в модели, а затем передать этот текст в <код> QClipboard .

Чтобы получить доступ к текстовым данным в модели, используйте QModelIndex :: data () . По умолчанию используется аргумент Qt :: DisplayRole , т.е. отображаемый текст.

Получив этот текст, передайте его в буфер обмена, используя QClipboard :: SetText () .

пример 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))

Я написал код, основанный на ответах других. Я вложил в подкласс QTableWidget и переопределил keyPressEvent () , чтобы позволить пользователю копировать выбранные строки в буфер обмена, набрав 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);
    }
}

Пример вывода (через табуляцию):

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

Наконец-то я понял, спасибо.

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

и

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

Не могу не заметить, что вы можете упростить свой код, используя <код > конструкция foreach () и QStringList класс, который имеет удобный join () функция.

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

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

Будьте осторожны с последним элементом.Обратите внимание ниже, что индексы могут стать пустыми после 'removeFirst()'.Таким образом, 'current' никогда не является допустимым и не должен использоваться в model()->data(current).

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

Рассмотреть

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

Вот вариант того, что опубликовал Корвин Джой, который работает с QTableView и по-разному обрабатывает разреженные выборки. С помощью этого кода, если у вас есть разные столбцы, выбранные в разных строках (например, выбранные ячейки (1,1), (1, 2), (2, 1), (3,2)), то при вставке вы получите пустой ячейки, соответствующие «отверстиям»; в вашем выборе (например, ячейки (2,2) и (3,1)). Он также извлекает текст заголовка столбца для столбцов, которые пересекают выбор.

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);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top