How to avoid to delete treemodel info when editing?

174 views Asked by At

Good morning. I have finally created a treeconfig program to read a txt, then edit it in the interface, and then save it, but I have a problem.

I know that there is a treemodel example that is editable (http://doc.qt.io/qt-5/qtwidgets-itemviews-editabletreemodel-example.html), but this one is not using libconfig.h++

I decided to use libconfig.h++ in my linux because I can filter which kind of information is appearing in the values (int/string/...), because in the other example without libconfig, it uses qVariant, and accept all the types of inputs.

So even comparing with the other example, I am not able to make the following work: My problem is that when I try to edit one value, the information inside dissapears, and if I click outside the value, it becomes 0. How can I avoid this both things? I want to edit a string in the middle for example, or just not to delete then info when missclicking or something.

The images below show an attempt at editing the value for confTimeout. Note that the text inside disappears when it is clicked into edit-mode and that the text sets to 0 when it is clicked out of focus.

mainwindow.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();
}

treeitem.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; }

treemodel

    #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;
}

o

o

o

o

EDIT:::

First of all, thanks @vahancho and @trebuchetMS . I have tried with vahancho example, but it throws an issue

enter image description here

Also I tried with with other combinations, but he idea is not to allow for example a "string" where a "int" should be placed.

I have also checked again the editableTreeModel of QtExamples, but it allows a string where a int should be placed, and I do not want this

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());
}

Any idea?

1

There are 1 answers

1
vahancho On

I think the problem is that your TreeModel::data() function, according to the code you provided, always returns an empty data when requested role is not display role. This means that if you bring a node into edit mode the editor (line edit) shows nothing. When you move the focus, the editor closes and sets its empty value to the model. That's why you get this '0'.

To fix this behavior you need to expose the same data in edit mode as in display mode, i.e. you code should look like:

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);
}