MessageDialog - 从 C++ 更改文本但屏幕上没有更新

MessageDialog - text change from c++ but no update on screen

我已经设法动态更改包含 MessageDialog 的 QML Item 中的几个属性。我可以读取新设置的属性,但它们不会在屏幕上更新 - 它仍然是可见的 QML 设置的文本。我什至手动发送了 textChanged() 信号(见下文)无济于事。

知道我错过了什么吗?这是我的做法:

"ErrorDialog.qml":

Item{
    id: baseItem
    property string text: "myText"
    property string title: "myTitle"
    signal acceptedSignal()
    onTextChanged: {
        errorDialogItem.text=baseItem.text
    }
    MessageDialog {
        id: errorDialogItem
        objectName: "errorDialogItem"
        text: baseItem.text
        title: baseItem.title
        onAccepted: {
            acceptedSignal()
        }
    Component.onCompleted: visible=true
}

现在 c++:

this->component = QSharedPointer<QQmlComponent>(new QQmlComponent(&this->engine));
QObject::connect(&engine, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit()));
component->loadUrl(QUrl(QStringLiteral("qrc:///main.qml")));

topLevel = QSharedPointer<QObject>(component->create());
window = qobject_cast<QQuickWindow *>(topLevel.data());

surfaceFormat = window->requestedFormat();
window->setFormat(surfaceFormat);
window->show(); 

QQmlComponent dialogComponent(&engine, QUrl(QStringLiteral("qrc:///ErrorDialog.qml")));
QObject *myObject = dialogComponent.create();
QQuickItem *item = qobject_cast<QQuickItem*>(myObject);

QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership);
qDebug()<<"title"<<item->property("title");
qDebug()<<"text"<<item->property("text");
QString newText="TEXT SET FROM C++";
qDebug()<<"setTextProp: "<<item->setProperty("text",newText);
//-> true
qDebug()<<"new text"<<item->property("text"); 
//-> correctly set!

属性 已设置,但视觉上未更改。 所以我获取了嵌入式 MessageDialog 本身:

QObject* dialogObject=item->findChild<QObject*>("errorDialogItem");
QQuickItem* dialogItem=reinterpret_cast<QQuickItem*>(dialogObject);

item->setFlag(QQuickItem::ItemHasContents,true);
dialogItem->setFlag(QQuickItem::ItemHasContents,true);

我正在尽我所能,更新屏幕上的文本:

dialogItem->polish();
item->polish();

int methodIndex=item->metaObject()->indexOfMethod("textChanged()"); 
QMetaMethod method = item->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(item,Qt::DirectConnection);
//-->true

methodIndex=item->metaObject()->indexOfMethod("update()"); 
method = item->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(item,Qt::DirectConnection);
//-->true

methodIndex=dialogItem->metaObject()->indexOfMethod("textChanged()"); 
method = dialogItem->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection);
//-->true   

methodIndex=dialogItem->metaObject()->indexOfMethod("windowGeometryChanged()"); 
method = dialogItem->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection);
//-->true

methodIndex=dialogItem->metaObject()->indexOfMethod("geometryChanged()"); 
method = dialogItem->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection);
//-->true

methodIndex=dialogItem->metaObject()->indexOfMethod("reset()"); 
method = dialogItem->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection);
//-->true

methodIndex=dialogItem->metaObject()->indexOfMethod("setVisible()"); 
method = dialogItem->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection,Q_ARG(bool,falseVar));
//-->false

methodIndex=dialogItem->metaObject()->indexOfMethod("setVisible()"); 
method = dialogItem->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection, Q_ARG(bool,trueVar));
//-->false

methodIndex=dialogItem->metaObject()->indexOfMethod("setText()"); 
method = dialogItem->metaObject()->method(methodIndex);
QString textStr = "a different text";
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection, Q_ARG(QString,textStr));
//-->false

methodIndex=dialogItem->metaObject()->indexOfMethod("polish()"); 
method = dialogItem->metaObject()->method(methodIndex);
qDebug()<<"invoke: "<<method.invoke(dialogItem,Qt::DirectConnection);
//-->false

感谢@BaCaRoZzo,在创建最小示例时我发现了问题所在。在打开 Dialog 之前 属性 更改 绝对是强制性的,即处于 关闭 状态。如果我想更改已打开的 MessageDialog 中的内容,我必须关闭它,更改属性,然后再次打开它。更改属性然后关闭并重新打开(如 "redraw")是不够的。参见

bool setItemProperty(QString role, T value)

在下面的 .h 文件中。

这是我的工作代码,非常感谢任何进一步的建议。注意:"Controller" class 只是一些 class 提供了 GUI 的 QQmlApplicationEngine

C++-Class header: MessageDialog.h

#ifndef MESSAGEDIALOG_H
#define MESSAGEDIALOG_H
#include <QObject>
#include <QQuickItem>
#include <QDialog>
#include <QQmlApplicationEngine>
#include "src/controller/controller.h"

class MessageDialog : public QObject
{
    Q_OBJECT
public:
    enum StandardButton {
       Ok = 0x00000400,         //  An "OK" button with AcceptRole.
       Open = 0x00002000,       //  An "Open" button with AcceptRole.
       Save = 0x00000800,       //  A "Save" button with AcceptRole.
       Cancel = 0x00400000,     //  A "Cancel" button with RejectRole.
       Close = 0x00200000,      //  A "Close" button with RejectRole.
       Discard = 0x00800000,    //  A "Discard" button, DestructiveRole.
       Apply = 0x02000000,      //  An "Apply" button with ApplyRole.
       Reset = 0x04000000,      //  A "Reset" button with ResetRole.
       RestoreDefaults = 0x08000000,//"Restore Defaults", ResetRole.
       Help = 0x01000000,       //  A "Help" button with HelpRole.
       SaveAll = 0x00001000,    //  A "Save All" button with AcceptRole.
       Yes = 0x00004000,        //  A "Yes" button with YesRole.
       YesToAll = 0x00008000,   //  A "Yes to All" button with YesRole.
       No = 0x00010000,         //  A "No" button with NoRole.
       NoToAll = 0x00020000,    //  A "No to All" button with NoRole.
       Abort = 0x00040000,      //  An "Abort" button with RejectRole.
       Retry = 0x00080000,      //  A "Retry" button with AcceptRole.
       Ignore = 0x00100000,     //  An "Ignore" button with AcceptRole.
       NoButton = 0x00000000    //  An invalid button.
    };
    typedef QFlags<StandardButton> StandardButtons;

    enum ButtonRole {
        InvalidRole = -1,   //button is invalid.
        AcceptRole = 0,     //accepted (e.g. OK).
        RejectRole = 1,     //rejected (e.g. Cancel).
        DestructiveRole = 2,//destructive change 
        ActionRole = 3,     //changes dialog.
        HelpRole =  4,      //request help.
        YesRole =   5,      //Yes"-like button.
        NoRole =    6,      //"No"-like button.
        ApplyRole = 8,      //applies current changes.
        ResetRole = 7       //resets the dialog to default values.
    };
    enum Icon {
        NoIcon = 0,     // the message box does not have any icon.
        Question = 4,   // message is asking a question.
        Information = 1, // message is nothing out of the ordinary.
        Warning = 2,    // message is a warning, but can be dealt with.
        Critical = 3    // message represents a critical problem.
    };

    MessageDialog(Controller* controller,
            QString title=QString(),
            QString text=QString(),
            QString informativeText=QString(),
            QString detailedText=QString(),
            StandardButtons buttons=QFlags<StandardButton>());
    ~MessageDialog();

    void setText (const QString &text);
    void setInformativeText (const QString &text);
    void setDetailedText (const QString &text);
    void setTitle(const QString &title);
    bool open();
    bool close();

    void setStandardButtons(StandardButtons buttons);
    void addButton(StandardButton button);
    void setIcon(Icon icon);
    StandardButton clickedButton();

    void setModality(Qt::WindowModality modality);

    int result();
    StandardButton exec();

    static void about(Controller* controller,
            const QString &title,
            const QString &text,
            const QString &informativeText="",
            const QString &detailedText="");
    static StandardButton warning(
            Controller* controller,
            const QString &title,
            const QString &text,
            const QString &informativeText,
            const QString &detailedText,
            StandardButton button0 = NoButton,
            StandardButton button1 = NoButton,
            StandardButton button2 = NoButton,
            StandardButton button3 = NoButton);

    static MessageDialog::StandardButton information(
            Controller* controller,
            const QString &title,
            const QString &text,
            const QString &informativeText,
            const QString &detailedText,
            StandardButton button0 = NoButton,
            StandardButton button1 = NoButton,
            StandardButton button2 = NoButton,
            StandardButton button3 = NoButton);


signals:
        void buttonClicked(StandardButton standardButton);
        void accepted();
        void apply();
        void discard();
        void help();
        void no();
        void rejected();
        void reset();
        void yes();
public slots:



protected:
    void initItem(Controller* controller);
    void connectQMLSignals();
    bool invokeMethod(QString methodSignature);
    bool setStrings(
            const QString &title,
            const QString &text,
            const QString informativeText,
            const QString detailedText);
    template <typename T>
    bool setItemProperty(QString role, T value) {
        Q_ASSERT(m_item);
        bool wasOpen=m_isOpen;
        close();
        bool success;
        success=m_item->setProperty(role.toLocal8Bit(),QVariant(value));
        if(!success)
            qDebug()<<"setProperty("<<role<<","<<value<<") failed.";
        if (wasOpen) open();
        return success;
    }

    QQmlApplicationEngine* m_engine;
    QQuickItem* m_item;
    bool m_isOpen;
    StandardButtons m_standardButtons;
    StandardButton m_result;
};

#endif // MESSAGEDIALOG_H

C++-Class 实现:MessageDialog.cpp

#include "messagedialog.h"
#include <QQuickItem>
#include <QQmlApplicationEngine>
MessageDialog::MessageDialog(
        Controller *controller,
        QString title,
        QString text,
        QString informativeText,
        QString detailedText,
        StandardButtons buttons)
{
    Q_ASSERT(controller);
    this->initItem(controller);
    m_isOpen=false;
    m_result=MessageDialog::NoButton;
    this->setTitle(title);
    this->setText(text);
    this->setInformativeText(informativeText);
    this->setDetailedText(detailedText);
    this->setStandardButtons(buttons);
    this->connectQMLSignals();
}

MessageDialog::~MessageDialog()
{
    delete m_item;

}

void MessageDialog::setText(const QString &text)
{
    setItemProperty("text",text);
}

void MessageDialog::setInformativeText(const QString &text)
{
    setItemProperty("informativeText",text);
}

void MessageDialog::setDetailedText(const QString &text)
{
    setItemProperty("detailedText",text);
}

void MessageDialog::setTitle(const QString &title)
{
    setItemProperty("title",title);
}

void MessageDialog::setStandardButtons(StandardButtons buttons)
{
    setItemProperty("standardButtons",buttons);
    m_standardButtons=buttons;
}

void MessageDialog::addButton(MessageDialog::StandardButton button)
{
    m_standardButtons|=button;
    setItemProperty("standardButtons",m_standardButtons);
}


void MessageDialog::setIcon(MessageDialog::Icon icon)
{
    setItemProperty("icon",icon);
}

MessageDialog::StandardButton MessageDialog::clickedButton()
{
    int i=m_item->property("clickedButton").toInt();
    return static_cast<MessageDialog::StandardButton>(i);
}

void MessageDialog::setModality(Qt::WindowModality modality)
{
    setItemProperty("modality",modality);
}

int MessageDialog::result()
{
    return this->clickedButton();
}

MessageDialog::StandardButton MessageDialog::exec()
{
    QEventLoop loop;

    connect(m_item, SIGNAL(accepted()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    connect(m_item, SIGNAL(apply()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    connect(m_item, SIGNAL(discard()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    connect(m_item, SIGNAL(help()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    connect(m_item, SIGNAL(no()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    connect(m_item, SIGNAL(rejected()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    connect(m_item, SIGNAL(reset()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    connect(m_item, SIGNAL(yes()),& loop,
           SLOT(quit()),Qt::UniqueConnection);
    invokeMethod("open()");
    setModality(Qt::ApplicationModal);
    loop.exec();
    return this->clickedButton();
}

void MessageDialog::about(
        Controller *controller,
        const QString &title,
        const QString &text,
        const QString &informativeText,
        const QString &detailedText)

{
    MessageDialog dialog(controller);
    dialog.setStrings(title,text,informativeText,detailedText);
    dialog.setIcon(MessageDialog::Information);
    dialog.setStandardButtons(Ok);
    dialog.exec();
}

MessageDialog::StandardButton MessageDialog::warning(
        Controller *controller,
        const QString &title,
        const QString &text,
        const QString &informativeText,
        const QString &detailedText,
        MessageDialog::StandardButton button0,
        MessageDialog::StandardButton button1,
        MessageDialog::StandardButton button2,
        MessageDialog::StandardButton button3)
{
    MessageDialog dialog(controller);
    dialog.setStrings(title,text,informativeText,detailedText);
    dialog.setIcon(MessageDialog::Warning);
    dialog.setStandardButtons(
           StandardButtons(button0|button1|button2|button3));
    dialog.exec();
    return dialog.clickedButton();
}

MessageDialog::StandardButton MessageDialog::information(
        Controller *controller,
        const QString &title,
        const QString &text,
        const QString &informativeText,
        const QString &detailedText,
        MessageDialog::StandardButton button0,
        MessageDialog::StandardButton button1,
        MessageDialog::StandardButton button2,
        MessageDialog::StandardButton button3)
{
    MessageDialog dialog(controller);
    dialog.setStrings(title,text,informativeText,detailedText);
    dialog.setIcon(MessageDialog::Information);
    dialog.setStandardButtons(
           StandardButtons(button0|button1|button2|button3));
    dialog.exec();
    return dialog.clickedButton();
}

bool MessageDialog::open()
{
    Q_ASSERT(m_item);
    if (m_isOpen) return false;
    m_isOpen=invokeMethod("open()");
    return m_isOpen;
}

bool MessageDialog::close()
{
    Q_ASSERT(m_item);
    if (!m_isOpen) return false;
    m_isOpen=!invokeMethod("close()");
    return !m_isOpen;
}

bool MessageDialog::invokeMethod(QString methodSignature)
{
    Q_ASSERT(m_item);
    int methodIndex;
    methodIndex=m_item->metaObject()->indexOfMethod(
                                       methodSignature.toLocal8Bit());
    QMetaMethod method = m_item->metaObject()->method(methodIndex);
    return method.invoke(m_item,Qt::DirectConnection);
}

bool MessageDialog::setStrings(
        const QString &title,
        const QString &text,
        const QString informativeText,
        const QString detailedText)
{
    bool success=true;
    success &= this->setItemProperty("title",title);
    success &= this->setItemProperty("text",text);
    success &= this->setItemProperty("informativeText",informativeText);
    success &= this->setItemProperty("detailedText",detailedText);
    return success;
}

void MessageDialog::initItem(Controller *controller)
{
    m_engine=controller->engine();
    Q_ASSERT(m_engine);
    QQmlComponent* myComponent=new QQmlComponent(
           m_engine, QUrl(QStringLiteral("qrc:/MessageDialog.qml")));
    Q_ASSERT(myComponent);
        if (!myComponent->isReady()) {
        qWarning("%s", qPrintable(myComponent->errorString()));
    }
    QObject *myObject = myComponent->create();
    Q_ASSERT(myObject);
    m_item = qobject_cast<QQuickItem*>(myObject);
    Q_ASSERT(m_item);
    QQmlApplicationEngine::setObjectOwnership(
               m_item,QQmlApplicationEngine::CppOwnership);

}

void MessageDialog::connectQMLSignals()
{
    connect(m_item, SIGNAL(accepted()),this, SIGNAL(accepted()));
    connect(m_item, SIGNAL(apply()),this, SIGNAL(apply()));
    connect(m_item, SIGNAL(discard()),this, SIGNAL(discard()));
    connect(m_item, SIGNAL(help()),this, SIGNAL(help()));
    connect(m_item, SIGNAL(no()),this, SIGNAL(no()));
    connect(m_item, SIGNAL(rejected()),this, SIGNAL(rejected()));
    connect(m_item, SIGNAL(reset()),this, SIGNAL(reset()));
    connect(m_item, SIGNAL(yes()),this, SIGNAL(yes()));
}

这是通讯员MessageDialog.qml

import QtQuick 2.4
import QtQuick.Controls 1.3
import QtQuick.Dialogs 1.2
Item{
    id: baseItem
    property alias text: errorDialogItem.text
    property alias detailedText: errorDialogItem.detailedText
    property alias informativeText: errorDialogItem.informativeText
    property alias title: errorDialogItem.title
    property alias modality: errorDialogItem.modality
    property alias standardButtons: errorDialogItem.standardButtons
    property alias icon: errorDialogItem.icon
    property alias clickedButton: errorDialogItem.clickedButton
    property alias standardIconSource: 
                       errorDialogItem.standardIconSource

    signal accepted()
    signal apply()
    signal discard()
    signal help()
    signal no()
    signal rejected()
    signal reset()
    signal yes()


    function open() {
        return errorDialogItem.open()
    }
    function close() {
        return errorDialogItem.close()
    }
    MessageDialog {
        id: errorDialogItem
        objectName: "errorDialogItem"

        onAccepted: baseItem.accepted()
        onApply: baseItem.apply()
        onDiscard: baseItem.discard()
        onHelp: baseItem.help()
        onNo: baseItem.no()
        onRejected: baseItem.rejected()
        onReset: baseItem.reset()
        onYes: baseItem.yes()

    }

}

我希望这可以帮助将来有人节省时间。

这是 SeDi 答案的替代方法。它没有完全开发,但可以根据您自己的需要简单轻松地进行修改。

示例实现

 MessageDialog::showCritcalError( myQmlEngine, "The sky is falling!" );

MessageDialog.h

#ifndef MESSAGEDIALOG_H
#define MESSAGEDIALOG_H

#include <QQmlEngine>

struct MessageDialog
{    
    static void showCritcalError( QQmlEngine *engine, const QString &message );
};

#endif // MESSAGEDIALOG_H

MessageDialog.cpp

#include "MessageDialog.h"

#include <QQmlComponent>
#include <QQuickItem>

// ---------------------------------

static const QString QML_RESOURCE_PREFIX( "qrc:/" );
static const QString DIALOG_QML( "MessgeDialog.qml" );

static const char *TITLE_PROP   = "dialogTitle";
static const char *ICON_PROP    = "dialogIcon";
static const char *TEXT_PROP    = "dialogText";
static const char *VISIBLE_PROP = "dialogVisible";

static const QString INFO_DEFAULT_TITLE(     "Information" );
static const QString WARNING_DEFAULT_TITLE(  "Warning" );
static const QString CRITICAL_DEFAULT_TITLE( "Error" );
static const QString QUESTION_DEFAULT_TITLE( "Question" );

static const int NO_ICON = 0,
               INFO_ICON = 1,
                 WARNING = 2,
           CRITICAL_ICON = 3,
           QUESTION_ICON = 4;

void showMessageDialog( QQmlEngine *engine,
    const QString &title, const int icon, const QString &text  )
{
    QQmlComponent component( engine, QUrl( QML_RESOURCE_PREFIX + DIALOG_QML ) );
    QQuickItem *item = qobject_cast<QQuickItem*>( component.create() );
    item->setProperty( TITLE_PROP,   title );
    item->setProperty( ICON_PROP,    icon );
    item->setProperty( TEXT_PROP,    text );
    item->setProperty( VISIBLE_PROP, true ); // must do last after setting other properties
}

// ---------------------------------

void MessageDialog::showCritcalError( QQmlEngine *engine, const QString &message )
{ showMessageDialog( engine, CRITICAL_DEFAULT_TITLE, CRITICAL_ICON, message ); }

MessageDialog.qml(在我的用例中列在 .qrc 中)

import QtQuick 2.2
import QtQuick.Dialogs 1.1

Item {
    property string dialogTitle: ""
    property int     dialogIcon: 0
    property string  dialogText: ""
    property bool dialogVisible: false
    MessageDialog {
        id: messageDialog
        title   : dialogTitle
        icon    : dialogIcon
        text    : dialogText
        visible : dialogVisible
        modality: Qt.ApplicationModal
    }
}