首页 > 解决方案 > 编辑时如何避免删除树模型信息?

问题描述

早上好。我终于创建了一个treeconfig程序来读取一个txt,然后在界面中编辑它,然后保存它,但是我有一个问题。

我知道有一个可编辑的树模型示例(http://doc.qt.io/qt-5/qtwidgets-itemviews-editabletreemodel-example.html),但是这个没有使用 libconfig.h++

我决定在我的 linux 中使用 libconfig.h++,因为我可以过滤值中出现的信息类型(int/string/...),因为在另一个没有 libconfig 的示例中,它使用 qVariant,并接受所有类型的输入。

因此,即使与另一个示例相比,我也无法进行以下工作:我的问题是,当我尝试编辑一个值时,里面的信息消失了,如果我在值之外单击,它变成了 0。我怎么能避免这两种情况?例如,我想在中间编辑一个字符串,或者只是在单击错误时不删除信息。

下图显示了编辑 confTimeout 值的尝试。请注意,当它被单击进入编辑模式时,里面的文本会消失,并且当它被单击失焦时,文本会设置为 0。

主窗口.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QTextStream>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

void MainWindow::setConfig(libconfig::Config *config)
{
    tm = std::make_unique<TreeModel>(config, this);
    ui->treeView->setModel(tm.get());
}

MainWindow::~MainWindow()
{
    delete ui;
}

/*
void MainWindow::on_treeView_activated(const QModelIndex &index)
{
    QString val = ui->treeView->model()->data(index).toString();
    ui->lineEdit->setText(val);
}

*/

void MainWindow::on_pushButton_clicked()
{
    tm->saveSettings();
}

树项.cpp

#include "treeitem.h"
#include <libconfig.h++>

static QVariant getIndexOrName(const libconfig::Setting &setting)
{
    if (!setting.isRoot()) {
        const auto &parent = setting.getParent();
        if (parent.isArray() || parent.isList())
            return setting.getIndex();
    }

    return setting.getName();
}

static QVariant getValueOrSize(const libconfig::Setting &setting)
{
    using namespace libconfig;
    switch (setting.getType()) {
    // scalar types
    case Setting::TypeInt:
        return setting.operator int();

    case Setting::TypeInt64:
        return setting.operator long long();

    case Setting::TypeFloat:
        return setting.operator double();

    case Setting::TypeString:
        return setting.c_str();

    case Setting::TypeBoolean:
        return setting.operator bool();


    // aggregate types
    case Setting::TypeGroup:
        return QString{"Group. Size: %1"}.arg(setting.getLength());

    case Setting::TypeArray:
        return QString{"Array. Size: %1"}.arg(setting.getLength());

    case Setting::TypeList:
        return QString{"List. Size: %1"}.arg(setting.getLength());


    // not used
    case Setting::TypeNone:
        break;
    }

    return QVariant{};
}

static bool setValue(libconfig::Setting &setting, const QVariant &value)
{
    using namespace libconfig;
    switch (setting.getType()) {
    // scalar types
    case Setting::TypeInt:
        if (value.canConvert(QVariant::Int)) {
            setting = value.toInt();
            return true;
        }
    case Setting::TypeInt64:
        if (value.canConvert(QVariant::LongLong)) {
            setting = value.toLongLong();
            return true;
        }
    case Setting::TypeFloat:
        if (value.canConvert(QVariant::Double)) {
            setting = value.toFloat();
            return true;
        }
    case Setting::TypeString:
        if (value.canConvert(QVariant::String)) {
            setting = value.toString().toStdString();
            return true;
        }
    case Setting::TypeBoolean:
        if (value.canConvert(QVariant::Bool)) {
            setting = value.toBool();
            return true;
        }
    default:
        break;
    }

    return false;
}

TreeItem::TreeItem(libconfig::Setting *setting, TreeItem *parentItem)
    : m_setting{setting}, m_parentItem{parentItem}
{
    if (setting->isAggregate()) {
        for (auto &setting : *setting) {
            m_subSettings.push_back(new TreeItem(&setting, this));
        }
    }
}

TreeItem::~TreeItem() { qDeleteAll(m_subSettings); }

TreeItem *TreeItem::child(int row) { return m_subSettings.at(row); }

int TreeItem::childCount() const { return m_subSettings.size(); }

int TreeItem::columnCount() const { return 2; }

QVariant TreeItem::data(int column) const
{
    switch (column) {
    case 0:
        return getIndexOrName(*m_setting);
    case 1:
        return getValueOrSize(*m_setting);
    default:
        return QVariant{};
    }
}

bool TreeItem::setData(const QVariant &value)
{
    if (m_setting->isAggregate())
        return false;

    return setValue(*m_setting, value);
}

int TreeItem::row() const
{
    if (!m_parentItem)
        return 0;

    return m_parentItem->m_subSettings.indexOf(const_cast<TreeItem *>(this));
}

TreeItem *TreeItem::parentItem() { return m_parentItem; }

树模型

    #include "treemodel.h"
    #include "treeitem.h"

    #include <QFile>
    #include <libconfig.h++>
    #include <QDateTime>

    TreeModel::TreeModel(libconfig::Config *config, QObject *parent)
        : QAbstractItemModel{parent}, m_rootSetting{std::make_unique<TreeItem>(
                                                        &(config->getRoot()), nullptr)}, m_config{config}
    {
    }

    TreeModel::~TreeModel()
    {
    }

    QVariant TreeModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
            return QVariant();

        if (role != Qt::DisplayRole)
            return QVariant();

        TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

        return item->data(index.column());
    }

    bool TreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
    {
        if (!index.isValid())
            return false;

        if (role != Qt::EditRole)
            return false;

        TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
        return item->setData(value);
    }

    Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
    {

        if (!index.isValid())
            //return Qt::NoItemFlags;
            return 0;

        //return QAbstractItemModel::flags(index);
        return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
    }

    QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                   int role) const
    {
        if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
            return QVariant{};

        switch (section) {
        case 0:
            return "Name";
        case 1:
            return "Value";
        default:
            return QVariant{};
        }
    }

QModelIndex TreeModel::index(int row, int column,
                             const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    TreeItem *parentItem;

    if (!parent.isValid())
        parentItem = m_rootSetting.get();
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());

    TreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex TreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    TreeItem *childItem = static_cast<TreeItem *>(index.internalPointer());
    TreeItem *parentItem = childItem->parentItem();

    if (parentItem == m_rootSetting.get())
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int TreeModel::rowCount(const QModelIndex &parent) const
{
    TreeItem *parentItem;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = m_rootSetting.get();
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());

    return parentItem->childCount();
}

int TreeModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
    else
        return m_rootSetting->columnCount();
}

bool TreeModel::saveSettings(QString filename) const
{
    if (filename.isEmpty())
        filename = QString::fromLocal8Bit(m_config->getRoot().getSourceFile());
    QString today = QDateTime::currentDateTime().toString("_yyyy.MM.dd_hh:mm");
    QFile::copy(filename, filename+today+".backup");
    try {
        m_config->writeFile(filename.toLocal8Bit().constData());
    } catch (...) {
        return false;
    }

    return true;
}

编辑:::

首先,感谢@vahancho 和@trebuchetMS。我尝试过使用 vahancho 示例,但它引发了一个问题

在此处输入图像描述

我也尝试了其他组合,但他的想法是不允许例如应该放置“int”的“string”。

我还再次检查了 QtExamples 的 editableTreeModel,但它允许一个字符串应该放置一个 int,我不想要这个

QVariant TreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole && role != Qt::EditRole)
        return QVariant();

    TreeItem *item = getItem(index);

    return item->data(index.column());
}

任何想法?

标签: c++qtqt5qtreeviewlibconfig

解决方案


我认为问题在于TreeModel::data(),根据您提供的代码,当请求的角色不是显示角色时,您的函数总是返回空数据。这意味着如果您将节点带入编辑模式,则编辑器(行编辑)不会显示任何内容。当您移动焦点时,编辑器将关闭并将其空值设置为模型。这就是为什么你得到这个'0'。

要解决此问题,您需要在编辑模式和显示模式下公开相同的数据,即您的代码应如下所示:

QVariant TreeModel::data(const QModelIndex &index, int role) const
{
  if (!index.isValid())
    return QVariant();

  // Returns the same data both in display and edit modes.
  if (role == Qt::DisplayRole || role == Qt::EditRole)
  {
    TreeItem *item = static_cast<TreeItem *>(index.internalPointer());
    return item->data(index.column());
  }
  return QAbstractItemModel::data(index, role);
}

推荐阅读