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

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

My first approach was to generate the html code for the table and paste 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 inserting, table cells that are simply appended to each other and inserted without formatting.

My second approach using RTF:

QTextDocument rtfDocument;


But I haven't found a way to copy this QTextDocument to the clipboard. Whether there is a? If I could get the RTF code from 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 that returns rtf code.


With the latest code above, I have a working way to copy the rtf code to the clipboard. So any solution that can generate RTF code representing the table will solve my problem.


source to share

3 answers

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



I'm not sure what the source of your data is, but here is the code we used to subclass the regular QTableView

one to make it copyable. Some of the code has been cut, but you can get the basic idea. RTF / HTML is overflowing - all spreadsheets accept good ol CSV.

Of course this answer won't help at all if you need formatting. I was not clear from your question if this 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();

  if(indexes.isEmpty()) {

  // The default sort is by rows then columns. This is what we want.

  // 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 =
             GetViewColumnIndex(, &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 =;

    // Do we need to add a new line character?
    if (previous.isValid() && current.row() != previous.row()) {

    // 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.
                  .repeated(GetViewColumnIndex(current.column(), &map_cache) -
    } else {
      // Add enough separators to get from the previous to the current column.
                  .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 =
    if (delegate) {
      text.append(csvEscape(delegate->displayText(, QLocale())));
    } else {

    previous = current;


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

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

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





I wrote in gedit 1[tab space]2[tab space]3\n4[tab space]5[tab space]6

and copied it to a spreadsheet and it worked. So if you use "\ t" to separate cells in lines and "\ n" to separate lines, it will work.



All Articles