Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-17 10:29:02

0001 // Copyright(c) 2015-present, Gabi Melman, mguludag and spdlog contributors.
0002 // Distributed under the MIT License (http://opensource.org/licenses/MIT)
0003 
0004 #pragma once
0005 
0006 //
0007 // Custom sink for QPlainTextEdit or QTextEdit and its children (QTextBrowser...
0008 // etc) Building and using requires Qt library.
0009 //
0010 // Warning: the qt_sink won't be notified if the target widget is destroyed.
0011 // If the widget's lifetime can be shorter than the logger's one, you should provide some permanent
0012 // QObject, and then use a standard signal/slot.
0013 //
0014 
0015 #include "spdlog/common.h"
0016 #include "spdlog/details/log_msg.h"
0017 #include "spdlog/details/synchronous_factory.h"
0018 #include "spdlog/sinks/base_sink.h"
0019 #include <array>
0020 
0021 #include <QPlainTextEdit>
0022 #include <QTextEdit>
0023 
0024 //
0025 // qt_sink class
0026 //
0027 namespace spdlog {
0028 namespace sinks {
0029 template <typename Mutex>
0030 class qt_sink : public base_sink<Mutex> {
0031 public:
0032     qt_sink(QObject *qt_object, std::string meta_method)
0033         : qt_object_(qt_object),
0034           meta_method_(std::move(meta_method)) {
0035         if (!qt_object_) {
0036             throw_spdlog_ex("qt_sink: qt_object is null");
0037         }
0038     }
0039 
0040     ~qt_sink() { flush_(); }
0041 
0042 protected:
0043     void sink_it_(const details::log_msg &msg) override {
0044         memory_buf_t formatted;
0045         base_sink<Mutex>::formatter_->format(msg, formatted);
0046         const string_view_t str = string_view_t(formatted.data(), formatted.size());
0047         QMetaObject::invokeMethod(
0048             qt_object_, meta_method_.c_str(), Qt::AutoConnection,
0049             Q_ARG(QString, QString::fromUtf8(str.data(), static_cast<int>(str.size())).trimmed()));
0050     }
0051 
0052     void flush_() override {}
0053 
0054 private:
0055     QObject *qt_object_ = nullptr;
0056     std::string meta_method_;
0057 };
0058 
0059 // Qt color sink to QTextEdit.
0060 // Color location is determined by the sink log pattern like in the rest of spdlog sinks.
0061 // Colors can be modified if needed using sink->set_color(level, qtTextCharFormat).
0062 // max_lines is the maximum number of lines that the sink will hold before removing the oldest
0063 // lines. By default, only ascii (latin1) is supported by this sink. Set is_utf8 to true if utf8
0064 // support is needed.
0065 template <typename Mutex>
0066 class qt_color_sink : public base_sink<Mutex> {
0067 public:
0068     qt_color_sink(QTextEdit *qt_text_edit,
0069                   int max_lines,
0070                   bool dark_colors = false,
0071                   bool is_utf8 = false)
0072         : qt_text_edit_(qt_text_edit),
0073           max_lines_(max_lines),
0074           is_utf8_(is_utf8) {
0075         if (!qt_text_edit_) {
0076             throw_spdlog_ex("qt_color_text_sink: text_edit is null");
0077         }
0078 
0079         default_color_ = qt_text_edit_->currentCharFormat();
0080         // set colors
0081         QTextCharFormat format;
0082         // trace
0083         format.setForeground(dark_colors ? Qt::darkGray : Qt::gray);
0084         colors_.at(level::trace) = format;
0085         // debug
0086         format.setForeground(dark_colors ? Qt::darkCyan : Qt::cyan);
0087         colors_.at(level::debug) = format;
0088         // info
0089         format.setForeground(dark_colors ? Qt::darkGreen : Qt::green);
0090         colors_.at(level::info) = format;
0091         // warn
0092         format.setForeground(dark_colors ? Qt::darkYellow : Qt::yellow);
0093         colors_.at(level::warn) = format;
0094         // err
0095         format.setForeground(Qt::red);
0096         colors_.at(level::err) = format;
0097         // critical
0098         format.setForeground(Qt::white);
0099         format.setBackground(Qt::red);
0100         colors_.at(level::critical) = format;
0101     }
0102 
0103     ~qt_color_sink() { flush_(); }
0104 
0105     void set_default_color(QTextCharFormat format) {
0106         // std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
0107         default_color_ = format;
0108     }
0109 
0110     void set_level_color(level::level_enum color_level, QTextCharFormat format) {
0111         // std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
0112         colors_.at(static_cast<size_t>(color_level)) = format;
0113     }
0114 
0115     QTextCharFormat &get_level_color(level::level_enum color_level) {
0116         std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
0117         return colors_.at(static_cast<size_t>(color_level));
0118     }
0119 
0120     QTextCharFormat &get_default_color() {
0121         std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
0122         return default_color_;
0123     }
0124 
0125 protected:
0126     struct invoke_params {
0127         invoke_params(int max_lines,
0128                       QTextEdit *q_text_edit,
0129                       QString payload,
0130                       QTextCharFormat default_color,
0131                       QTextCharFormat level_color,
0132                       int color_range_start,
0133                       int color_range_end)
0134             : max_lines(max_lines),
0135               q_text_edit(q_text_edit),
0136               payload(std::move(payload)),
0137               default_color(default_color),
0138               level_color(level_color),
0139               color_range_start(color_range_start),
0140               color_range_end(color_range_end) {}
0141         int max_lines;
0142         QTextEdit *q_text_edit;
0143         QString payload;
0144         QTextCharFormat default_color;
0145         QTextCharFormat level_color;
0146         int color_range_start;
0147         int color_range_end;
0148     };
0149 
0150     void sink_it_(const details::log_msg &msg) override {
0151         memory_buf_t formatted;
0152         base_sink<Mutex>::formatter_->format(msg, formatted);
0153 
0154         const string_view_t str = string_view_t(formatted.data(), formatted.size());
0155         // apply the color to the color range in the formatted message.
0156         QString payload;
0157         int color_range_start = static_cast<int>(msg.color_range_start);
0158         int color_range_end = static_cast<int>(msg.color_range_end);
0159         if (is_utf8_) {
0160             payload = QString::fromUtf8(str.data(), static_cast<int>(str.size()));
0161             // convert color ranges from byte index to character index.
0162             if (msg.color_range_start < msg.color_range_end) {
0163                 color_range_start = QString::fromUtf8(str.data(), msg.color_range_start).size();
0164                 color_range_end = QString::fromUtf8(str.data(), msg.color_range_end).size();
0165             }
0166         } else {
0167             payload = QString::fromLatin1(str.data(), static_cast<int>(str.size()));
0168         }
0169 
0170         invoke_params params{max_lines_,             // max lines
0171                              qt_text_edit_,          // text edit to append to
0172                              std::move(payload),     // text to append
0173                              default_color_,         // default color
0174                              colors_.at(msg.level),  // color to apply
0175                              color_range_start,      // color range start
0176                              color_range_end};       // color range end
0177 
0178         QMetaObject::invokeMethod(
0179             qt_text_edit_, [params]() { invoke_method_(params); }, Qt::AutoConnection);
0180     }
0181 
0182     void flush_() override {}
0183 
0184     // Add colored text to the text edit widget. This method is invoked in the GUI thread.
0185     // It is a static method to ensure that it is handled correctly even if the sink is destroyed
0186     // prematurely before it is invoked.
0187 
0188     static void invoke_method_(invoke_params params) {
0189         auto *document = params.q_text_edit->document();
0190         QTextCursor cursor(document);
0191 
0192         // remove first blocks if number of blocks exceeds max_lines
0193         while (document->blockCount() > params.max_lines) {
0194             cursor.select(QTextCursor::BlockUnderCursor);
0195             cursor.removeSelectedText();
0196             cursor.deleteChar();  // delete the newline after the block
0197         }
0198 
0199         cursor.movePosition(QTextCursor::End);
0200         cursor.setCharFormat(params.default_color);
0201 
0202         // if color range not specified or not not valid, just append the text with default color
0203         if (params.color_range_end <= params.color_range_start) {
0204             cursor.insertText(params.payload);
0205             return;
0206         }
0207 
0208         // insert the text before the color range
0209         cursor.insertText(params.payload.left(params.color_range_start));
0210 
0211         // insert the colorized text
0212         cursor.setCharFormat(params.level_color);
0213         cursor.insertText(params.payload.mid(params.color_range_start,
0214                                              params.color_range_end - params.color_range_start));
0215 
0216         // insert the text after the color range with default format
0217         cursor.setCharFormat(params.default_color);
0218         cursor.insertText(params.payload.mid(params.color_range_end));
0219     }
0220 
0221     QTextEdit *qt_text_edit_;
0222     int max_lines_;
0223     bool is_utf8_;
0224     QTextCharFormat default_color_;
0225     std::array<QTextCharFormat, level::n_levels> colors_;
0226 };
0227 
0228 #include "spdlog/details/null_mutex.h"
0229 #include <mutex>
0230 
0231 using qt_sink_mt = qt_sink<std::mutex>;
0232 using qt_sink_st = qt_sink<details::null_mutex>;
0233 using qt_color_sink_mt = qt_color_sink<std::mutex>;
0234 using qt_color_sink_st = qt_color_sink<details::null_mutex>;
0235 }  // namespace sinks
0236 
0237 //
0238 // Factory functions
0239 //
0240 
0241 // log to QTextEdit
0242 template <typename Factory = spdlog::synchronous_factory>
0243 inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name,
0244                                             QTextEdit *qt_object,
0245                                             const std::string &meta_method = "append") {
0246     return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
0247 }
0248 
0249 template <typename Factory = spdlog::synchronous_factory>
0250 inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name,
0251                                             QTextEdit *qt_object,
0252                                             const std::string &meta_method = "append") {
0253     return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
0254 }
0255 
0256 // log to QPlainTextEdit
0257 template <typename Factory = spdlog::synchronous_factory>
0258 inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name,
0259                                             QPlainTextEdit *qt_object,
0260                                             const std::string &meta_method = "appendPlainText") {
0261     return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
0262 }
0263 
0264 template <typename Factory = spdlog::synchronous_factory>
0265 inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name,
0266                                             QPlainTextEdit *qt_object,
0267                                             const std::string &meta_method = "appendPlainText") {
0268     return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
0269 }
0270 // log to QObject
0271 template <typename Factory = spdlog::synchronous_factory>
0272 inline std::shared_ptr<logger> qt_logger_mt(const std::string &logger_name,
0273                                             QObject *qt_object,
0274                                             const std::string &meta_method) {
0275     return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
0276 }
0277 
0278 template <typename Factory = spdlog::synchronous_factory>
0279 inline std::shared_ptr<logger> qt_logger_st(const std::string &logger_name,
0280                                             QObject *qt_object,
0281                                             const std::string &meta_method) {
0282     return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
0283 }
0284 
0285 // log to QTextEdit with colorized output
0286 template <typename Factory = spdlog::synchronous_factory>
0287 inline std::shared_ptr<logger> qt_color_logger_mt(const std::string &logger_name,
0288                                                   QTextEdit *qt_text_edit,
0289                                                   int max_lines,
0290                                                   bool is_utf8 = false) {
0291     return Factory::template create<sinks::qt_color_sink_mt>(logger_name, qt_text_edit, max_lines,
0292                                                              false, is_utf8);
0293 }
0294 
0295 template <typename Factory = spdlog::synchronous_factory>
0296 inline std::shared_ptr<logger> qt_color_logger_st(const std::string &logger_name,
0297                                                   QTextEdit *qt_text_edit,
0298                                                   int max_lines,
0299                                                   bool is_utf8 = false) {
0300     return Factory::template create<sinks::qt_color_sink_st>(logger_name, qt_text_edit, max_lines,
0301                                                              false, is_utf8);
0302 }
0303 
0304 }  // namespace spdlog