2009-08-04 4 views
15

Прежде всего, извините за плохой английский.выбранных строк/строк в копии QTableView в QClipboard

Речь идет о C++ и Qt. У меня есть SQLite-Database, и я сделал это в QSqlTableModel. Чтобы показать базу данных, я поместил эту модель в QTableView.

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

Но у меня нет идеи, что делать с «Selected» -SIGNAL и QModelIndex и как поместить это в буфер обмена.

Так вы можете мне помочь?

Berschi

ответ

24

Чтобы получить максимальный отбор, вы используете selection model, чтобы получить list of indices. Учитывая, что у вас есть QTableView * под названием view вы попадёте в меню выбора таким образом:

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

Тогда цикл по индексного списка вызывающего model->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(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 эквивалент работы.

+0

извините, но я не понимаю, как использовать QItemSelectionModel и QModelIndexList в этом случае с Model и TableView – Berschi

+0

Здесь вы также можете использовать удобную функцию QAbstractItemView :: selectedIndexes() который доступен в вашем QTableView (в котором QAbstractItemView является родителем). Возвращается простой контейнер списка объектов QModelIndex. – swongu

+0

Я немного расширил пример, чтобы проиллюстрировать, что описывает swongu. – quark

1

Что вам нужно сделать, это получить доступ к текстовой информации в модели, а затем передать этот текст в QClipboard.

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

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

+0

спасибо, это действительно помогло, пока. Я добавил следующий код: connect (tableView, SIGNAL (щелкнул (QModelIndex)), это, SLOT (копия (QModelIndex))); и следующее: void Widget :: copy (QModelIndex sel) { clipboard-> setText ((sel.data (Qt :: DisplayRole)). ToString()); } это отлично работает для одной строки. Но если я выберу 2 или более строк, это не сработает. Как скопировать больше строк или целую строку в буфер обмена? – Berschi

+0

О, извините, я действительно новый здесь ... и с рядом (комментарий выше) Я имел в виду ячейку. Извините :( – Berschi

+0

Поскольку вы используете clicked (QModelIndex), это только вернет ячейку, на которую пользователь нажал. Если вы хотите скопировать текст из всех выбранных ячеек, вы должны использовать предлагаемый кварк решения. – swongu

0

Я, наконец, получил его, спасибо.

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

Если вы используете QStringList, вы можете воспользоваться функцией << operator и QStringList :: join(). – swongu

0

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

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

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

спасибо за помощь, метод foreach был для меня новичком – Berschi

13

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

qtablewidgetwithcopypaste.ч

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

} 
+0

Еще две ссылки, которые могут быть полезны, - http://books.google.com/books?id=tSCR_4LH2KsC&pg=PA101&lpg=PA101&dq=copy+paste&source=web&ots=E511hS4aCk&sig = EYaYF4Er89UKAnKFRZLHYgtTdO4 # PPP1, M1 и http://stackoverflow.com/a/12979530/999943 и – phyatt

4

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

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

с

qSort(indexes); 

Это то же самое, как написание:

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

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

0

Осторожно с последним элементом. Примечание ниже, индексы могут стать пустыми после 'removeFirst()'. Таким образом, «текущий» никогда не действует и не должен использоваться в 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()); 
1

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

Я написал код, основанный на некоторых ответах других. Я подклассифицировал 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 
0

Вот вариация на тему того, что отправил Корвин Джой, который работает с 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); 
} 
5

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

Давайте предположим, что наш пример таблица выглядит так:

| B | C
D | E | F

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

Если мы заменим его \ т сепаратор с '| ', он будет производить этот выход:
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); 

Поэтому я решил использовать счетчик вместо итератора только потому, что легче проверить, если существует другой индекс, проверяя против графа. С итератором, я полагаю, возможно, вы могли бы просто увеличить его и сохранить в слабом указателе, чтобы проверить, действительно ли он действителен, но просто используйте счетчик, как я сделал выше.

Мы должны проверить, будет ли линия следующей линии в новой строке. Если мы находимся на новой строке, и мы проверяем предыдущую строку как алгоритм Quark, ее уже слишком поздно добавлять. Мы могли бы добавить, но тогда нам нужно отслеживать последний размер строки. Вышеприведенный код будет производить следующий результат из таблицы примеров:

A | B | C
D | E | F