QSyntaxHighlighter rehighlight()忽略从另一个线程触发的更改

时间:2018-04-23 13:18:06

标签: qt qml syntax-highlighting

我有一个拼写检查线程,可以不时触发spellcheck()信号,这些信号与我的荧光笔的rehighlight()方法相关联。后者将整个块设置为红色前景。

这曾在Qt 5.6.2中使用,并且不再适用于较新版本。我绝望地等待它在Qt 5.9.5中得到修复,但它仍然不起作用(在Windows 10和OS X中都没有)。

可以从这里https://bitbucket.org/ribtoks/qt-highlighting-issue获得重现问题的小例子(为了重新编写,在输入中键入内容。rehighlight()将从后台线程每7秒触发一次)

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickTextDocument>
#include <QSyntaxHighlighter>
#include <QThread>
#include <QString>

class SpellCheckWorker : public QObject
{
    Q_OBJECT
public:
    explicit SpellCheckWorker(QObject *parent = 0) : QObject(parent), m_Counter(0), m_IsOK(false)
    { }

public:
    bool isOK() { return m_IsOK; }

signals:
    void spellcheck();

public slots:
    void process() {
        qInfo() << "Worker Thread is" << QThread::currentThreadId();
        while (1) {
            m_Counter++;
            m_IsOK = m_Counter % 7 == 0;
            QThread::sleep(1);
            emit spellcheck();
        }
    }

private:
    int m_Counter;
    volatile bool m_IsOK;
};

class SpellCheckErrorsHighlighter : public QSyntaxHighlighter
{
    Q_OBJECT
public:
    SpellCheckErrorsHighlighter(SpellCheckWorker *worker, QTextDocument *document):
        QSyntaxHighlighter(document),
        m_Worker(worker)
    { }
    virtual ~SpellCheckErrorsHighlighter() {}

protected:
    virtual void highlightBlock(const QString &text) override {
        if (!m_Worker->isOK()) {
            qDebug() << "Worker is not OK" << text;
            return;
        }

        qInfo() << "Reapplied formatting for" << text;
        qInfo() << "Highlight thread is" << QThread::currentThreadId();

        this->setFormat(0, text.length(), QColor(0xff, 0, 0));
    }

private:
    SpellCheckWorker *m_Worker;
};

class MainModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
public:
    explicit MainModel(QObject *parent = 0) : QObject(parent), m_Worker(nullptr)
    { }

public:
    QString name() const { return m_name; }
    void startChecking() {
        qInfo() << "Main thread is" << QThread::currentThreadId();
        m_Worker = new SpellCheckWorker();
        QThread *thread = new QThread();
        m_Worker->moveToThread(thread);

        QObject::connect(thread, &QThread::started, m_Worker, &SpellCheckWorker::process);
        thread->start();
    }

    Q_INVOKABLE void initNameHighlighting(QQuickTextDocument *document) {
        SpellCheckErrorsHighlighter *highlighter = new SpellCheckErrorsHighlighter(m_Worker, document->textDocument());
        QObject::connect(m_Worker, &SpellCheckWorker::spellcheck,
                         highlighter, &SpellCheckErrorsHighlighter::rehighlight);
    }

signals:
    void nameChanged();

public slots:
    void setName(QString name)
    {
        if (m_name == name)
            return;

        m_name = name;
        emit nameChanged();
    }

private:
    SpellCheckWorker *m_Worker;
    QString m_name;
};

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    MainModel mainModel;
    mainModel.startChecking();

    QQmlApplicationEngine engine;

    QQmlContext *rootContext = engine.rootContext();
    rootContext->setContextProperty("mainModel", &mainModel);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    return app.exec();
}

#include "main.moc"

main.qml

import QtQuick 2.6
import QtQuick.Controls 1.0
import QtQuick.Layouts 1.3
import QtQuick.Window 2.0

ApplicationWindow  {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    StackView {
        id: mainStackView
        anchors.fill: parent
        focus: true

        initialItem: Rectangle {
            anchors.fill: parent

            Rectangle {
                id: titleRect
                height: 30
                width: 300
                anchors.centerIn: parent
                color: "#ffffff"
                border.color: "#000000"
                border.width: titleTextInput.activeFocus ? 1 : 0
                clip: true
                focus: false

                Flickable {
                    id: titleFlick
                    contentWidth: titleTextInput.paintedWidth
                    contentHeight: titleTextInput.paintedHeight
                    height: parent.height
                    anchors.verticalCenter: parent.verticalCenter
                    anchors.left: parent.left
                    anchors.right: parent.right
                    anchors.leftMargin: 5
                    anchors.rightMargin: 5
                    clip: true
                    flickableDirection: Flickable.HorizontalFlick
                    interactive: false
                    focus: false

                    function ensureVisible(r) {
                        if (contentX >= r.x)
                            contentX = r.x;
                        else if (contentX+width <= r.x+r.width)
                            contentX = r.x+r.width-width;
                    }

                    TextEdit {
                        id: titleTextInput
                        width: paintedWidth > titleFlick.width ? paintedWidth : titleFlick.width
                        height: titleFlick.height
                        text: mainModel.name
                        focus: true
                        onTextChanged: mainModel.name = text

                        Component.onCompleted: mainModel.initNameHighlighting(titleTextInput.textDocument)
                        onCursorRectangleChanged: titleFlick.ensureVisible(cursorRectangle)
                    }
                }
            }
        }
    }
}

有没有办法让它使用变通方法?我需要在后台线程中保留拼写检查逻辑,因此无法将其移动到主线程。

2 个答案:

答案 0 :(得分:-1)

您似乎指的是两个不同的根本原因,并带有以下两个陈述:

  

向荧光笔提供插槽调用没有问题。

  

m_Worker-&gt; isOK()检查是否有理由每7秒执行一次突出显示。 Qt中的错误只能用这个证明。

如果我只关注第二个语句,看起来问题是你无法点击语句if (!m_Worker->isOK())吗?

由于您的代码或特定平台上的Qt问题,这可能是一个问题。 您是否可以更改代码以避免出现这种情况,即仅在7秒后发出spellcheck信号以避免从另一个线程进行此检查?

答案 1 :(得分:-3)

好的,所以在弄乱你的代码之后,问题是你没有在第55行附近的main.cpp中的hilightBlock指令中调用这一行,以保持之前的格式。

添加此内容以修复它(我认为)问题仍然相当不明确......

if (!m_Worker->isOK()) {
    qDebug() << "Worker is not OK" << text;
    this->setFormat(0, text.length(), this->format(0));
    return;
}

问题的另一部分是您没有为语法高亮显示器定义任何规则...因此一旦变为红色它将始终为红色。