本文介绍了C ++ Qt:从线程重定向cout以发出信号的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在一个线程中,我有一个漂亮的类,它将所有cout输出重定向到QTextEdit

In a single thread, I have this beautiful class that redirects all cout output to a QTextEdit

#include <iostream>
#include <streambuf>
#include <string>
#include <QScrollBar>

#include "QTextEdit"
#include "QDateTime"

class ThreadLogStream : public std::basic_streambuf<char>, QObject
{
    Q_OBJECT
public:
    ThreadLogStream(std::ostream &stream) : m_stream(stream)
    {
        m_old_buf = stream.rdbuf();
        stream.rdbuf(this);
    }
    ~ThreadLogStream()
    {
        // output anything that is left
        if (!m_string.empty())
        {
            log_window->append(m_string.c_str());
        }

        m_stream.rdbuf(m_old_buf);
    }

protected:
    virtual int_type overflow(int_type v)
    {
        if (v == '\n')
        {
            log_window->append(m_string.c_str());
            m_string.erase(m_string.begin(), m_string.end());
        }
        else
            m_string += v;

        return v;
    }


    virtual std::streamsize xsputn(const char *p, std::streamsize n)
    {
        m_string.append(p, p + n);

        long pos = 0;
        while (pos != static_cast<long>(std::string::npos))
        {
            pos = m_string.find('\n');
            if (pos != static_cast<long>(std::string::npos))
            {
                std::string tmp(m_string.begin(), m_string.begin() + pos);
                log_window->append(tmp.c_str());
                m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
            }
        }

        return n;
    }

private:
    std::ostream &m_stream;
    std::streambuf *m_old_buf;
    std::string m_string;


    QTextEdit* log_window;
};

但是,如果使用cout启动ANY线程(QThread),则此方法不起作用.这是因为所有指针都弄乱了,并且必须使用信号和插槽来允许子线程和主线程之间的数据传输.

However, this doesn't work if ANY thread (QThread) is initiated with a cout. This is because all pointers are messed up, and one has to use signals and slots for allowing transfer of data between the sub-thread and the main thread.

我想修改此类以发出信号,而不是写入文本文件.这要求该类成为Q_OBJECT并从一个继承.除了std::basic_streambuf<char>之外,我还尝试从QObject继承,并在主体中添加了Q_OBJECT宏,但未编译.

I would like to modify this class to emit a signal rather than write to a text file. This requires that this class becomes a Q_OBJECT and be inherited from one. I tried to inherit from QObject in addition to std::basic_streambuf<char> and added Q_OBJECT macro in the body but it didn't compile.

能否请您帮助我实现这一目标?我应该怎么做才能使此类发出可以连接的并且是线程安全的信号?

Could you please help me to achieve this? What should I do to get this class to emit signals that I can connect to and that are thread safe?

推荐答案

派生需要先发生QObject -first:

The derivation needs to happen QObject-first:

class LogStream : public QObject, std::basic_streambuf<char> {
  Q_OBJECT
  ...
};
...

如果目标是最小地修改您的代码,则有一种更简单的方法.如果您确切知道信号将插入哪个插槽,则无需继承QObject即可发出信号.您所需要做的就是以线程安全的方式调用该插槽:

If the goal was to minimally modify your code, there's a simpler way. You don't need to inherit QObject to emit signals iff you know exactly what slots the signals are going to. All you need to do is to invoke the slot in a thread safe way:

QMetaObject::invokeMethod(log_window, "append", Qt::QueuedConnection,
                          Q_ARG(QString, tmp.c_str()));

要加快处理速度,可以缓存该方法,这样就不必每次都查找它:

To speed things up, you can cache the method so that it doesn't have to be looked up every time:

class LogStream ... {
  QPointer<QTextEdit> m_logWindow;
  QMetaMethod m_append;

  LogStream::LogStream(...) :
    m_logWindow(...),
    m_append(m_logWindow->metaObject()->method(
             m_logWindow->metaObject()->indexOfSlot("append(QString)") )) {

    ...
  }
};

然后您可以更有效地调用它:

You can then invoke it more efficiently:

m_append.invoke(m_logWindow, Qt::QueuedConnection, Q_ARG(QString, tmp.c_str()));

最后,每当您持有生命周期不受您控制的对象的指针时,使用QPointer很有帮助,因为它永远不会晃动.当指向的对象被破坏时,QPointer会将自己重置为0.至少可以防止您取消引用悬空指针,因为它永远不会悬空.

Finally, whenever you're holding pointers to objects whose lifetimes are not under your control, it's helpful to use QPointer since it never dangles. A QPointer resets itself to 0 when the pointed-to object gets destructed. It will at least prevent you from dereferencing a dangling pointer, since it never dangles.

这篇关于C ++ Qt:从线程重定向cout以发出信号的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-05 07:19