summaryrefslogtreecommitdiffstats
path: root/examples/widgets
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-07-10 08:09:56 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2020-07-10 11:31:09 +0000
commita83b2c64a9828a1d22347eaf31fd251b2ef647ee (patch)
treeb8d5dae083dddd43c6565514db8e7cee754f91fd /examples/widgets
parent9ac5273d03c176156596a23d2a88cf95f0b4d6d6 (diff)
Polish the settingseditor example
The example is meant to show an item delegate with a line edit with QRegularExpression-based validation depending on type. Unfortunately, this does not work since QSettings mostly return QString types. Fix it to a partially working state by - Making the expressions match from beginning to end which was overlooked in the QRegExp->QRegularExpression change. - Use QCheckBox, QSpinBox for bool/int since it is silly to have a user edit a bool value by typing 'true'/'false'. - Move the expressions out to a separate struct to be able to do some guessing of the type when reading the QSettings, implement for bool and int. - Use a fancy Unicode checkmark for displaying bools. - Fix the garbled display of QByteArray with binary data by displaying them with hex characters and setting them read-only. Change-Id: Iba22dfafc3b813b3fd3d2915ef5210d661049382 Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Diffstat (limited to 'examples/widgets')
-rw-r--r--examples/widgets/tools/settingseditor/settingstree.cpp15
-rw-r--r--examples/widgets/tools/settingseditor/settingstree.h5
-rw-r--r--examples/widgets/tools/settingseditor/variantdelegate.cpp277
-rw-r--r--examples/widgets/tools/settingseditor/variantdelegate.h37
4 files changed, 227 insertions, 107 deletions
diff --git a/examples/widgets/tools/settingseditor/settingstree.cpp b/examples/widgets/tools/settingseditor/settingstree.cpp
index 9132368e4a..04af8ce3b9 100644
--- a/examples/widgets/tools/settingseditor/settingstree.cpp
+++ b/examples/widgets/tools/settingseditor/settingstree.cpp
@@ -57,9 +57,10 @@
#include <QSettings>
SettingsTree::SettingsTree(QWidget *parent)
- : QTreeWidget(parent)
+ : QTreeWidget(parent),
+ m_typeChecker(new TypeChecker)
{
- setItemDelegate(new VariantDelegate(this));
+ setItemDelegate(new VariantDelegate(m_typeChecker, this));
setHeaderLabels({tr("Setting"), tr("Type"), tr("Value")});
header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
@@ -77,6 +78,8 @@ SettingsTree::SettingsTree(QWidget *parent)
connect(&refreshTimer, &QTimer::timeout, this, &SettingsTree::maybeRefresh);
}
+SettingsTree::~SettingsTree() = default;
+
void SettingsTree::setSettingsObject(const SettingsPtr &newSettings)
{
settings = newSettings;
@@ -211,6 +214,14 @@ void SettingsTree::updateChildItems(QTreeWidgetItem *parent)
if (value.userType() == QMetaType::UnknownType) {
child->setText(1, "Invalid");
} else {
+ if (value.type() == QVariant::String) {
+ const QString stringValue = value.toString();
+ if (m_typeChecker->boolExp.match(stringValue).hasMatch()) {
+ value.setValue(stringValue.compare("true", Qt::CaseInsensitive) == 0);
+ } else if (m_typeChecker->signedIntegerExp.match(stringValue).hasMatch())
+ value.setValue(stringValue.toInt());
+ }
+
child->setText(1, value.typeName());
}
child->setText(2, VariantDelegate::displayText(value));
diff --git a/examples/widgets/tools/settingseditor/settingstree.h b/examples/widgets/tools/settingseditor/settingstree.h
index 3e9e9658ce..54b1ce6157 100644
--- a/examples/widgets/tools/settingseditor/settingstree.h
+++ b/examples/widgets/tools/settingseditor/settingstree.h
@@ -60,14 +60,18 @@ QT_BEGIN_NAMESPACE
class QSettings;
QT_END_NAMESPACE
+struct TypeChecker;
+
class SettingsTree : public QTreeWidget
{
Q_OBJECT
public:
using SettingsPtr = QSharedPointer<QSettings>;
+ using TypeCheckerPtr = QSharedPointer<TypeChecker>;
SettingsTree(QWidget *parent = nullptr);
+ ~SettingsTree();
void setSettingsObject(const SettingsPtr &settings);
QSize sizeHint() const override;
@@ -94,6 +98,7 @@ private:
void moveItemForward(QTreeWidgetItem *parent, int oldIndex, int newIndex);
SettingsPtr settings;
+ TypeCheckerPtr m_typeChecker;
QTimer refreshTimer;
QIcon groupIcon;
QIcon keyIcon;
diff --git a/examples/widgets/tools/settingseditor/variantdelegate.cpp b/examples/widgets/tools/settingseditor/variantdelegate.cpp
index eb822f0dc2..255f6135be 100644
--- a/examples/widgets/tools/settingseditor/variantdelegate.cpp
+++ b/examples/widgets/tools/settingseditor/variantdelegate.cpp
@@ -50,29 +50,83 @@
#include "variantdelegate.h"
+#include <QCheckBox>
#include <QDateTime>
#include <QLineEdit>
+#include <QSpinBox>
#include <QRegularExpressionValidator>
+#include <QTextStream>
-VariantDelegate::VariantDelegate(QObject *parent)
- : QStyledItemDelegate(parent)
+#include <algorithm>
+
+static bool isPrintableChar(char c)
+{
+ return uchar(c) >= 32 && uchar(c) < 128;
+}
+
+static bool isPrintable(const QByteArray &ba)
{
- boolExp.setPattern("true|false");
+ return std::all_of(ba.cbegin(), ba.cend(), isPrintableChar);
+}
+
+static QString byteArrayToString(const QByteArray &ba)
+{
+ if (isPrintable(ba))
+ return QString::fromLatin1(ba);
+ QString result;
+ for (char c : ba) {
+ if (isPrintableChar(c)) {
+ if (c == '\\')
+ result += QLatin1Char(c);
+ result += QLatin1Char(c);
+ } else {
+ const uint uc = uchar(c);
+ result += "\\x";
+ if (uc < 16)
+ result += '0';
+ result += QString::number(uc, 16);
+ }
+ }
+ return result;
+}
+
+TypeChecker::TypeChecker()
+{
+ boolExp.setPattern("^(true)|(false)$");
boolExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
+ Q_ASSERT(boolExp.isValid());
- byteArrayExp.setPattern("[\\x00-\\xff]*");
- charExp.setPattern(".");
- colorExp.setPattern("^\\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\\)$");
+ byteArrayExp.setPattern(R"RX(^[\x00-\xff]*$)RX");
+ charExp.setPattern("^.$");
+ Q_ASSERT(charExp.isValid());
+ colorExp.setPattern(R"RX(^\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\)$)RX");
+ Q_ASSERT(colorExp.isValid());
doubleExp.setPattern("");
- pointExp.setPattern("^\\((-?[0-9]*),(-?[0-9]*)\\)$");
- rectExp.setPattern("^\\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\\)$");
- signedIntegerExp.setPattern("-?[0-9]*");
+ pointExp.setPattern(R"RX(^\((-?[0-9]*),(-?[0-9]*)\)$)RX");
+ Q_ASSERT(pointExp.isValid());
+ rectExp.setPattern(R"RX(^\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\)$)RX");
+ Q_ASSERT(rectExp.isValid());
+ signedIntegerExp.setPattern("^-?[0-9]*$");
+ Q_ASSERT(signedIntegerExp.isValid());
sizeExp = pointExp;
- unsignedIntegerExp.setPattern("[0-9]*");
+ unsignedIntegerExp.setPattern("^[0-9]+$");
+ Q_ASSERT(unsignedIntegerExp.isValid());
- dateExp.setPattern("([0-9]{,4})-([0-9]{,2})-([0-9]{,2})");
- timeExp.setPattern("([0-9]{,2}):([0-9]{,2}):([0-9]{,2})");
- dateTimeExp.setPattern(dateExp.pattern() + 'T' + timeExp.pattern());
+ const QString datePattern = "([0-9]{,4})-([0-9]{,2})-([0-9]{,2})";
+ dateExp.setPattern('^' + datePattern + '$');
+ Q_ASSERT(dateExp.isValid());
+ const QString timePattern = "([0-9]{,2}):([0-9]{,2}):([0-9]{,2})";
+ timeExp.setPattern('^' + timePattern + '$');
+ Q_ASSERT(timeExp.isValid());
+ dateTimeExp.setPattern('^' + datePattern + 'T' + timePattern + '$');
+ Q_ASSERT(dateTimeExp.isValid());
+}
+
+VariantDelegate::VariantDelegate(const QSharedPointer<TypeChecker> &typeChecker,
+ QObject *parent)
+ : QStyledItemDelegate(parent),
+ m_typeChecker(typeChecker)
+{
}
void VariantDelegate::paint(QPainter *painter,
@@ -103,6 +157,26 @@ QWidget *VariantDelegate::createEditor(QWidget *parent,
if (!isSupportedType(originalValue.userType()))
return nullptr;
+ switch (originalValue.userType()) {
+ case QMetaType::Bool:
+ return new QCheckBox(parent);
+ break;
+ case QMetaType::Int:
+ case QMetaType::LongLong: {
+ auto spinBox = new QSpinBox(parent);
+ spinBox->setRange(-32767, 32767);
+ return spinBox;
+ }
+ case QMetaType::UInt:
+ case QMetaType::ULongLong: {
+ auto spinBox = new QSpinBox(parent);
+ spinBox->setRange(0, 63335);
+ return spinBox;
+ }
+ default:
+ break;
+ }
+
QLineEdit *lineEdit = new QLineEdit(parent);
lineEdit->setFrame(false);
@@ -110,45 +184,45 @@ QWidget *VariantDelegate::createEditor(QWidget *parent,
switch (originalValue.userType()) {
case QMetaType::Bool:
- regExp = boolExp;
+ regExp = m_typeChecker->boolExp;
break;
case QMetaType::QByteArray:
- regExp = byteArrayExp;
+ regExp = m_typeChecker->byteArrayExp;
break;
case QMetaType::QChar:
- regExp = charExp;
+ regExp = m_typeChecker->charExp;
break;
case QMetaType::QColor:
- regExp = colorExp;
+ regExp = m_typeChecker->colorExp;
break;
case QMetaType::QDate:
- regExp = dateExp;
+ regExp = m_typeChecker->dateExp;
break;
case QMetaType::QDateTime:
- regExp = dateTimeExp;
+ regExp = m_typeChecker->dateTimeExp;
break;
case QMetaType::Double:
- regExp = doubleExp;
+ regExp = m_typeChecker->doubleExp;
break;
case QMetaType::Int:
case QMetaType::LongLong:
- regExp = signedIntegerExp;
+ regExp = m_typeChecker->signedIntegerExp;
break;
case QMetaType::QPoint:
- regExp = pointExp;
+ regExp = m_typeChecker->pointExp;
break;
case QMetaType::QRect:
- regExp = rectExp;
+ regExp = m_typeChecker->rectExp;
break;
case QMetaType::QSize:
- regExp = sizeExp;
+ regExp = m_typeChecker->sizeExp;
break;
case QMetaType::QTime:
- regExp = timeExp;
+ regExp = m_typeChecker->timeExp;
break;
case QMetaType::UInt:
case QMetaType::ULongLong:
- regExp = unsignedIntegerExp;
+ regExp = m_typeChecker->unsignedIntegerExp;
break;
default:
break;
@@ -166,83 +240,102 @@ void VariantDelegate::setEditorData(QWidget *editor,
const QModelIndex &index) const
{
QVariant value = index.model()->data(index, Qt::UserRole);
- if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor))
+ if (auto spinBox = qobject_cast<QSpinBox *>(editor)) {
+ const auto userType = value.userType();
+ if (userType == QMetaType::UInt || userType == QMetaType::ULongLong)
+ spinBox->setValue(value.toUInt());
+ else
+ spinBox->setValue(value.toInt());
+ } else if (auto checkBox = qobject_cast<QCheckBox *>(editor)) {
+ checkBox->setChecked(value.toBool());
+ } else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor)) {
+ if (value.userType() == QMetaType::QByteArray
+ && !isPrintable(value.toByteArray())) {
+ lineEdit->setReadOnly(true);
+ }
lineEdit->setText(displayText(value));
+ }
}
void VariantDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const
{
- QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor);
- if (!lineEdit->isModified())
- return;
+ const QVariant originalValue = index.model()->data(index, Qt::UserRole);
+ QVariant value;
- QString text = lineEdit->text();
- const QValidator *validator = lineEdit->validator();
- if (validator) {
- int pos;
- if (validator->validate(text, pos) != QValidator::Acceptable)
+ if (auto spinBox = qobject_cast<QSpinBox *>(editor)) {
+ value.setValue(spinBox->value());
+ } else if (auto checkBox = qobject_cast<QCheckBox *>(editor)) {
+ value.setValue(checkBox->isChecked());
+ } else if (QLineEdit *lineEdit = qobject_cast<QLineEdit *>(editor)) {
+ if (!lineEdit->isModified())
return;
- }
-
- QVariant originalValue = index.model()->data(index, Qt::UserRole);
- QVariant value;
- QRegularExpressionMatch match;
- switch (originalValue.userType()) {
- case QMetaType::QChar:
- value = text.at(0);
- break;
- case QMetaType::QColor:
- match = colorExp.match(text);
- value = QColor(qMin(match.captured(1).toInt(), 255),
- qMin(match.captured(2).toInt(), 255),
- qMin(match.captured(3).toInt(), 255),
- qMin(match.captured(4).toInt(), 255));
- break;
- case QMetaType::QDate:
- {
- QDate date = QDate::fromString(text, Qt::ISODate);
- if (!date.isValid())
+ QString text = lineEdit->text();
+ const QValidator *validator = lineEdit->validator();
+ if (validator) {
+ int pos;
+ if (validator->validate(text, pos) != QValidator::Acceptable)
return;
- value = date;
}
- break;
- case QMetaType::QDateTime:
- {
- QDateTime dateTime = QDateTime::fromString(text, Qt::ISODate);
- if (!dateTime.isValid())
- return;
- value = dateTime;
- }
- break;
- case QMetaType::QPoint:
- match = pointExp.match(text);
- value = QPoint(match.captured(1).toInt(), match.captured(2).toInt());
- break;
- case QMetaType::QRect:
- match = rectExp.match(text);
- value = QRect(match.captured(1).toInt(), match.captured(2).toInt(),
- match.captured(3).toInt(), match.captured(4).toInt());
- break;
- case QMetaType::QSize:
- match = sizeExp.match(text);
- value = QSize(match.captured(1).toInt(), match.captured(2).toInt());
- break;
- case QMetaType::QStringList:
- value = text.split(',');
- break;
- case QMetaType::QTime:
- {
- QTime time = QTime::fromString(text, Qt::ISODate);
- if (!time.isValid())
- return;
- value = time;
+
+ QRegularExpressionMatch match;
+
+ switch (originalValue.userType()) {
+ case QMetaType::QChar:
+ value = text.at(0);
+ break;
+ case QMetaType::QColor:
+ match = m_typeChecker->colorExp.match(text);
+ value = QColor(qMin(match.captured(1).toInt(), 255),
+ qMin(match.captured(2).toInt(), 255),
+ qMin(match.captured(3).toInt(), 255),
+ qMin(match.captured(4).toInt(), 255));
+ break;
+ case QMetaType::QDate:
+ {
+ QDate date = QDate::fromString(text, Qt::ISODate);
+ if (!date.isValid())
+ return;
+ value = date;
+ }
+ break;
+ case QMetaType::QDateTime:
+ {
+ QDateTime dateTime = QDateTime::fromString(text, Qt::ISODate);
+ if (!dateTime.isValid())
+ return;
+ value = dateTime;
+ }
+ break;
+ case QMetaType::QPoint:
+ match = m_typeChecker->pointExp.match(text);
+ value = QPoint(match.captured(1).toInt(), match.captured(2).toInt());
+ break;
+ case QMetaType::QRect:
+ match = m_typeChecker->rectExp.match(text);
+ value = QRect(match.captured(1).toInt(), match.captured(2).toInt(),
+ match.captured(3).toInt(), match.captured(4).toInt());
+ break;
+ case QMetaType::QSize:
+ match = m_typeChecker->sizeExp.match(text);
+ value = QSize(match.captured(1).toInt(), match.captured(2).toInt());
+ break;
+ case QMetaType::QStringList:
+ value = text.split(',');
+ break;
+ case QMetaType::QTime:
+ {
+ QTime time = QTime::fromString(text, Qt::ISODate);
+ if (!time.isValid())
+ return;
+ value = time;
+ }
+ break;
+ default:
+ value = text;
+ value.convert(originalValue.userType());
}
- break;
- default:
- value = text;
- value.convert(originalValue.userType());
}
model->setData(index, displayText(value), Qt::DisplayRole);
@@ -279,7 +372,9 @@ QString VariantDelegate::displayText(const QVariant &value)
{
switch (value.userType()) {
case QMetaType::Bool:
+ return value.toBool() ? "✓" : "☐";
case QMetaType::QByteArray:
+ return byteArrayToString(value.toByteArray());
case QMetaType::QChar:
case QMetaType::Double:
case QMetaType::Int:
diff --git a/examples/widgets/tools/settingseditor/variantdelegate.h b/examples/widgets/tools/settingseditor/variantdelegate.h
index 96e44fd181..9d46aa77a5 100644
--- a/examples/widgets/tools/settingseditor/variantdelegate.h
+++ b/examples/widgets/tools/settingseditor/variantdelegate.h
@@ -53,13 +53,34 @@
#include <QStyledItemDelegate>
#include <QRegularExpression>
+#include <QSharedPointer>
+
+struct TypeChecker
+{
+ TypeChecker();
+
+ QRegularExpression boolExp;
+ QRegularExpression byteArrayExp;
+ QRegularExpression charExp;
+ QRegularExpression colorExp;
+ QRegularExpression dateExp;
+ QRegularExpression dateTimeExp;
+ QRegularExpression doubleExp;
+ QRegularExpression pointExp;
+ QRegularExpression rectExp;
+ QRegularExpression signedIntegerExp;
+ QRegularExpression sizeExp;
+ QRegularExpression timeExp;
+ QRegularExpression unsignedIntegerExp;
+};
class VariantDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
- VariantDelegate(QObject *parent = nullptr);
+ explicit VariantDelegate(const QSharedPointer<TypeChecker> &typeChecker,
+ QObject *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
@@ -73,19 +94,7 @@ public:
static QString displayText(const QVariant &value);
private:
- mutable QRegularExpression boolExp;
- mutable QRegularExpression byteArrayExp;
- mutable QRegularExpression charExp;
- mutable QRegularExpression colorExp;
- mutable QRegularExpression dateExp;
- mutable QRegularExpression dateTimeExp;
- mutable QRegularExpression doubleExp;
- mutable QRegularExpression pointExp;
- mutable QRegularExpression rectExp;
- mutable QRegularExpression signedIntegerExp;
- mutable QRegularExpression sizeExp;
- mutable QRegularExpression timeExp;
- mutable QRegularExpression unsignedIntegerExp;
+ QSharedPointer<TypeChecker> m_typeChecker;
};
#endif