将运行时 QML 输出转发到 UI 的问题
Posted
技术标签:
【中文标题】将运行时 QML 输出转发到 UI 的问题【英文标题】:Issue with forwarding runtime QML output to the UI 【发布时间】:2016-09-21 18:50:10 【问题描述】:我正在开发this open source project,它基本上是一个实时 QML 解释器。由于用户的主要活动是编写 QML 代码,我想在应用程序中嵌入所有日志和警告等,以提供一些基本的类似 IDE 的功能。首先,我通过qInstallMessageHandler 安装了一个自定义消息处理程序,它接收来自 QML 的所有运行时消息:
auto Instance::HandleMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg) -> void
std::cout << "handle message: " << msg.toStdString() << std::endl;
Logger::GetInstance()->addEntry(msg);
Logger
的addEntry
方法只是将消息附加到QStringList 属性并发出相应的changed()
信号。在 QML 方面,我将该属性提供给 ListView:
ListView
anchors.fill: parent;
model: App.logger.entries;
delegate: Text
text: modelData;
这行得通。但问题与动态 QML 对象实例化结合出现。我的应用程序的“神奇”部分是对 Qt.createQmlObject 的调用,它需要一些 QML 源作为字符串并返回新创建的对象:
function compile()
try
qmlObject = Qt.createQmlObject(<qml_source>, container, "root");
catch (exc)
// handle *compile-time* errors
如果“编译”失败,控制立即转移到 catch 子句,并且不会创建新的 QML 对象。但如果我尝试从以下来源创建对象,应用程序会崩溃:
function compile()
try
qmlObject = Qt.createQmlObject("import QtQuick 2.6\nItem anchors.fill: parent; Rectangle width: parent.width; height: a.height; ", container, "root");
catch (exc)
// handle *compile-time* errors
上述来源有效,但会产生运行时错误。出于某种原因,HandleMessage 函数在这个特定场景中被调用了两次:
handle message: [...]App/root:2: ReferenceError: a is not defined
handle message: [...]App/root:2: ReferenceError: a is not defined
并创建一个绑定循环:
handle message: [...]/App/containerview.qml:76:9: QML Repeater: Binding loop detected for property "model"
这也由自定义消息处理程序处理...您看到这是怎么回事:应用程序陷入循环并最终崩溃。
到目前为止我的观察:
不将传入消息转发到 QStringList 属性(只是计算它们)只会导致一次自定义消息处理程序的调用。
在构造时间之后创建相同的错误不会造成任何问题:
qmlObject = Qt.createQmlObject("import QtQuick 2.6\nimport QtQuick.Controls 1.4\nItem anchors.fill: parent; Rectangle id: rect; width: parent.width; Button anchors.centerIn: parent; text: \"do something stupid\"; onClicked: rect.height = a.height; ", container, "root");
不使用基于列表的属性(例如将所有消息存储在单个QString 对象中)也可以解决问题,但有一些明显的缺点。
最后是问题: 有人能解释一下为什么会发生这种情况以及如何解决这个问题吗?
【问题讨论】:
我想知道拥有两个 QML 引擎会不会更好,一个运行 IDE,一个运行正在编辑的内容? @KevinKrammer 这实际上是一个好点!我试试看,谢谢! 【参考方案1】:您让消息处理程序重新进入。你不应该:
class Instance
QThreadStorage<bool> handlerEntered;
...
;
QThreadStorage<bool> Instance::handlerEntered;
void Instance::HandleMessage(
QtMsgType type, const QMessageLogContext& context, const QString& msg)
if (handlerEntered.localData()) return;
QScopedValueRollback<bool> roll(handlerEntered.localData(), true);
std::cout << "handle message: " << msg.toStdString() << std::endl;
Logger::GetInstance()->addEntry(msg);
您还必须确保Logger::addEntry
方法不会重新进入事件循环,也不会发出任何消息。最有可能的是,您需要将对changed
信号的调用排队:
void Logger::addEntry(const QString & msg)
...
QMetaObject::invokeMethod(this, "listChanged(QStringList)",
Qt::QueuedConnection, Q_ARG(QStringList, data));
否则,任何直接连接的插槽(如 QML 引擎)可能会在信号 - 和 addEntry
和 HandleMessage
仍在调用堆栈上时做很多事情。
【讨论】:
以上是关于将运行时 QML 输出转发到 UI 的问题的主要内容,如果未能解决你的问题,请参考以下文章