Thanks @BaCaRoZzo, when creating that minimal example I've found out what the problem is. It is absolutely mandatory to do property changes before opening the Dialog
, i.e. in a closed state. If I want to change things in a MessageDialog
already opened, I have to close it, change the properties and then open it again. It is not sufficient to change properties and then to close and reopen (like a "redraw"). See
bool setItemProperty(QString role, T value)
in the .h
file below.
Here is my working code, any further recommendations are gratefully appreciated. Note: the "Controller" class is just some class providing the QQmlApplicationEngine
of the GUI.
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 implementation: 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()));
}
And this is the correspondent 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()
}
}
I hope this might help somebody save time in the future.