0
votes

I'm formatting a string using QString::arg, and I need the string to be formatted in columns. For example:

78.0    78.0    78.0
0.0     0.0     78.0
69.0    56.0    0.0

Right now I'm usingd += QString("%1").arg("78.0", -50, ' '); for each column then d += '\n' for a new row.

The only problem is that the space character isn't the same width as the digits, so things become misaligned:

misalignment

All I want to know is how can I align the text into columns? Thanks.

1
What is that screenshot from? In other words, how are you displaying the text? Why not using a monospace font?peppe
You're asking the wrong thing. It's not about QString, it's about whatever you use to display text: it must support the alignment you desire. Please edit the question to show a self-contained example of how you're doing it - it should be <20 lines long, a single main.cpp.Kuba hasn't forgotten Monica
@peppe Ahh, I wasn't aware monospace fonts existed. How can I get my QTableWidget to use a monospace font?picklechips
Nevermind, after a quick google search i was able to use a monospace font. Perfect! Thank you!picklechips
This makes no sense. You don't need to use whitespace to align columns in a QTableWidget. What are you doing exactly? A QTableWidget or QTableView can display perfectly aligned columns! Please tell us exactly what you're trying to do first. Probably the how is completely wrong.Kuba hasn't forgotten Monica

1 Answers

1
votes

You could use a QTextEdit and its rich text layout capabilities. You can then generate an html table programmatically:

// https://github.com/KubaO/stackoverflown/tree/master/questions/textedit-columns-37949301
#include <QtWidgets>

template <typename It, typename F>
QString toTable(It begin, It end, int columns, F && format, 
                const QString & attributes = QString()) {
   if (begin == end) return QString();
   QString output = QStringLiteral("<table %1>").arg(attributes);
   int n = 0;
   for (; begin != end; ++begin) {
      if (!n) output += "<tr>";
      output += "<td>" + format(*begin) + "</td>";
      if (++n == columns) {
         n = 0;
         output += "</tr>";
      }
   }
   output += "</table>";
   return output;
}

If you're loading a lot of data into a QTextEdit, you can create a QTextDocument in a separate thread, fill it there, and then transfer it to QTextEdit:

#include <QtConcurrent>

void setHtml(QTextEdit * edit, const QString & html) {
  QtConcurrent::run([=]{
    // runs in a worker thread
    auto doc = new QTextDocument;
    doc->setHtml(html);
    doc->moveToThread(edit->thread());
    QObject src;
    src.connect(&src, &QObject::destroyed, qApp, [=]{
      // runs in the main thread
      doc->setParent(edit);
      edit->setDocument(doc);
    });
  });
}

In a similar manner it's possible to relegate the QTextEdit's rendering to a worker thread as well, although that's a matter I'd have to address in a separate question. The approach would be akin to the one in this answer.

Let's put it to use:

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   double const table[] {
      78.0,    78.0,    78.0,
      0.0,     0.0,     78.0,
      69.0,    56.0,    0.0};
   QTextEdit edit;
   setHtml(&edit, toTable(std::begin(table), std::end(table), 3,
                          [](double arg){ return QString::number(arg, 'f', 1); },
                          "width=\"100%\""));
   edit.setReadOnly(true);
   edit.show();
   return app.exec();
}