Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Copying a (rtf) table into the clipboard via QT (or: Writing a QTextDocument into clipboard)

I need my QT application to create a table and copy this table into the clipboard, so that it can be pasted as table into libreoffice Writer or MS Word later.

My first approach was to create html code for the table and insert it into the clipboard with

QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/html", html.toUtf8());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);

This approach didn't work. When pasting, the table cells where just appended to each other and inserted without formatting.

My second approach using RTF:

QTextDocument rtfDocument;
rtfDocument.setHtml(html);

But I found no way to copy this QTextDocument into the clipboard. Is there any? If I could get the RTF code out of the QTextDocument, I could use a way like

QClipboard *clipboard = QApplication::clipboard();
QMimeData *mimeData = new QMimeData();
mimeData->setData("text/rtf", rtfDocument.getCode());
clipboard->setMimeData(mimeData, QClipboard::Clipboard);

But I also didn't find a function returning the rtf code.

edit:

With the last code box above I have a working way to copy rtf code into the clipboard. So any solution that can create RTF code representing a table would solve my problem.

like image 784
Heinzi Avatar asked Nov 15 '25 16:11

Heinzi


2 Answers

I'm not sure what the source of your data is, but here is code we used to subclass the normal QTableView to make it copy-able. Some of the code has been cut out, but you can get the basic idea. RTF/HTML is overkill--all the spreadsheets accept good ol' CSV.

Of course, this answer won't help at all if you require formatting. I wasn't clear from your question if that was a requirement or not.

// Escapes a string according to RFC-4180 specification.
static QString csvEscape(const QString &value) {
  if (value.contains(QRegExp(QLatin1String("[\"\\n\\r,]")))) {
    QString escaped(value);
    escaped.replace(QLatin1String("\""), QLatin1String("\"\""));
    return QString::fromLatin1("\"%1\"").arg(escaped);
  } else {
    return value;
  }
}

void ClipboardAwareTableView::Copy() const {
  QModelIndexList indexes = selectedIndexes();

  Q_ASSERT(!indexes.isEmpty());
  if(indexes.isEmpty()) {
    return;
  }

  // The default sort is by rows then columns. This is what we want.
  qSort(indexes);

  // Remember the mapping between model columns and visible columns. This is
  // local instead of an instance member because it would need to be invalidated
  // any time a column is added, removed, or moved. The minor performance hit
  // is worth the simplicity.
  QHash<int, int> map_cache;

  // Before we start exporting text, we have to know the index of the left-
  // most column in our selection range so we can add the appropriate number
  // of column separators.
  int minimum_column = GetViewColumnIndex(indexes.first().column(), &map_cache);
  for (int i = 1; i < indexes.size(); ++i) {
    minimum_column =
        qMin(minimum_column,
             GetViewColumnIndex(indexes.at(i).column(), &map_cache));
  }

  // Keep track of the previous index so that we know if we need a new line and
  // how many column separators to insert. We start with an invalid index.
  QModelIndex previous;

  QString text;

  for (int i = 0; i < indexes.size(); ++i) {
    QModelIndex current = indexes.at(i);

    // Do we need to add a new line character?
    if (previous.isValid() && current.row() != previous.row()) {
      text.append(QLatin1String("\n"));
    }

    // Are we on a new line?
    if (!previous.isValid() || current.row() != previous.row()) {
      // Add enough separators to get from the minimum to the current column.
      text.append(QString::fromLatin1(",")
                  .repeated(GetViewColumnIndex(current.column(), &map_cache) -
                            minimum_column));
    } else {
      // Add enough separators to get from the previous to the current column.
      text.append(QString::fromLatin1(",")
                  .repeated(GetViewColumnIndex(current.column(), &map_cache) -
                            GetViewColumnIndex(previous.column(), &map_cache)));
    }

    // Append the text. If the column delegate is a QStyledItemDelegate, we use
    // the display text.
    QStyledItemDelegate *delegate =
        qobject_cast<QStyledItemDelegate*>(
          itemDelegateForColumn(current.column()));
    if (delegate) {
      text.append(csvEscape(delegate->displayText(current.data(), QLocale())));
    } else {
      text.append(csvEscape(current.data().toString()));
    }

    previous = current;
  }

  qApp->clipboard()->setText(text);
}

int ClipboardAwareTableView::GetViewColumnIndex(
    int model_column_index,
    QHash<int, int> *cached_mappings) const {
  if (cached_mappings->contains(model_column_index)) {
    return cached_mappings->value(model_column_index);
  }

  int view_index = 0;
  for (int i = 0; i < model()->columnCount(); ++i) {
    if (model_column_index == i) {
      cached_mappings->insert(model_column_index, view_index);
      return view_index;
    } else if (!isColumnHidden(i)) {
      ++view_index;
    }
  }

  throw std::invalid_argument("model_column_index was out of range.");
}

void ClipboardAwareTableView::keyPressEvent(QKeyEvent *event) {
  if (event->matches(QKeySequence::Copy) && !selectedIndexes().isEmpty()) {
    Copy();
    event->accept();
    return;  // The base class implementation will overwrite the clipboard.
  }

  event->ignore();
  QTableView::keyPressEvent(event);
}
like image 183
Dave Mateer Avatar answered Nov 18 '25 05:11

Dave Mateer


You could try using QTextDocument::toHtml() and set the mime type to text/html

like image 23
Attila Avatar answered Nov 18 '25 07:11

Attila



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!