Go back to Richel Bilderbeek's homepage.

Go back to Richel Bilderbeek's C++ page.

 

 

 

 

 

(C++) QtModel

 

QtQt CreatorLubuntu

 

QtModel are some Qt model classes.

Technical facts

 

 

 

 

 

 

./CppQtModel/CppQtModel.pri

 

INCLUDEPATH += \
    ../../Classes/CppQtModel

SOURCES += \
    ../../Classes/CppQtModel/modelfunctionparser.cpp \
    ../../Classes/CppQtModel/qtstdvectorfunctionmodel.cpp \
    ../../Classes/CppQtModel/qtstdvectorstringmodel.cpp \
    ../../Classes/CppQtModel/qtublasmatrixdoublemodel.cpp \
    ../../Classes/CppQtModel/qtublasvectordoublemodel.cpp \
    ../../Classes/CppQtModel/qtublasvectorintmodel.cpp

HEADERS  += \
    ../../Classes/CppQtModel/modelfunctionparser.h \
    ../../Classes/CppQtModel/qtstdvectorfunctionmodel.h \
    ../../Classes/CppQtModel/qtstdvectorstringmodel.h \
    ../../Classes/CppQtModel/qtublasmatrixdoublemodel.h \
    ../../Classes/CppQtModel/qtublasvectordoublemodel.h \
    ../../Classes/CppQtModel/qtublasvectorintmodel.h

 

 

 

 

 

./CppQtModel/modelfunctionparser.h

 

#ifndef MODELFUNCTIONPARSER_H
#define MODELFUNCTIONPARSER_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#include <boost/shared_ptr.hpp>
#pragma GCC diagnostic pop

struct FunctionParser;

namespace ribi {

///Uses Warp's function parser and adds a random function
struct ModelFunctionParser
{
  ///For example:
  ///my_function = 'x * x * sin(x) * rand(x)'
  ///variable_name = 'x'
  explicit ModelFunctionParser(std::string my_function, const std::string& variable_name);

  ModelFunctionParser(const ModelFunctionParser&e) = delete;
  ModelFunctionParser& operator=(const ModelFunctionParser&e) = delete;


  ///Calculate the y for 'y = f(x)'
  double Evaluate(const double x) const;

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  private:
  const boost::shared_ptr<FunctionParser> m_parser;

  static double MyRand(const double * const max) noexcept;

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif
};

} //~namespace ribi

#endif // MODELFUNCTIONPARSER_H

 

 

 

 

 

./CppQtModel/modelfunctionparser.cpp

 

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "modelfunctionparser.h"

#include <cassert>
#include <stdexcept>

#include <boost/math/constants/constants.hpp>

//Warp's function parser
#include "fparser.hh"

#include "ribi_random.h"
#include "templocale.h"
#include "testtimer.h"
#include "trace.h"
#pragma GCC diagnostic pop

ribi::ModelFunctionParser::ModelFunctionParser(
  std::string my_function,
  const std::string& variable_name)
  : m_parser{new FunctionParser}
{
  #ifndef NDEBUG
  Test();
  #endif
  assert(m_parser);

  const double pi = boost::math::constants::pi<double>();
  m_parser->AddConstant("pi",pi);
  m_parser->AddConstant("tau",2.0*pi);
  m_parser->AddFunction("rand",MyRand,1);

  //FunctionParser assumes the dot ('.') as a decimal seperator,
  //therefore, convert all comma's in my_function to dots
  //std::replace(std::begin(my_function),std::end(my_function),',','.');
  //assert(std::count(std::begin(my_function),std::end(my_function),',') == 0
  //  && "FunctionParser assumes dots as decimal seperator");

  m_parser->Parse(my_function,variable_name);
  if (m_parser->GetParseErrorType()!= FunctionParser::FP_NO_ERROR)
  {
    const std::string error
      = "InputFunctionParser cannot parse '"
      + my_function
      + "' with variable '"
      + variable_name
      + "' (note: this can have to do that FunctionParser assumes an English locale,"
      + "as '0.0' should be accepted)"
    ;
    #ifndef NDEBUG
    TRACE(my_function);
    TRACE(variable_name);
    #endif
    throw std::runtime_error(error.c_str());
  }
}

double ribi::ModelFunctionParser::Evaluate(const double x) const
{
  const double xs[1] = { x };
  const double y = m_parser->Eval(xs);
  return y;
}

std::string ribi::ModelFunctionParser::GetVersion() noexcept
{
  return "1.2";
}

std::vector<std::string> ribi::ModelFunctionParser::GetVersionHistory() noexcept
{
  return {
    "201x-xx-xx: version 1.0: initial version",
    "2015-01-03: version 1.1: set locale to English in Desktop version", //SET_LOCALE_TO_ENGLISH_MODELFUNCTIONPARSER
    "2015-01-04: version 1.2: made locale local"
  };
}

double ribi::ModelFunctionParser::MyRand(const double * const max) noexcept
{
  assert(max);
  return (*max) * Random().GetFraction();
}

#ifndef NDEBUG
void ribi::ModelFunctionParser::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  //Set std::locale to English
  TempLocale temp_english_locale("en_US.UTF-8");

  //In case the decimal digits need to be non-Dutch
  {
    const double pi = boost::math::constants::pi<double>();
    assert(boost::lexical_cast<std::string>(pi)[1] == '.' && "No Dutch please");
  }

  {
    const ModelFunctionParser p("x * x * sin(x) * rand(x)","x");
    p.Evaluate(0.0);
  }
  try
  {
    std::string zero{boost::lexical_cast<std::string>(0.0)};
    const ModelFunctionParser p(zero,"x");
    assert(std::abs(p.Evaluate(0.0) - 0.0) < 0.0001);
  }
  catch (std::runtime_error&)
  {
    assert(!"Should not get here, '0.0' is a correct function");
  }
}
#endif

 

 

 

 

 

./CppQtModel/qtstdvectorfunctionmodel.h

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#ifndef QTSTDVECTORFUNCTIONMODEL_H
#define QTSTDVECTORFUNCTIONMODEL_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/numeric/ublas/vector.hpp>
#include <QAbstractTableModel>
#pragma GCC diagnostic pop

namespace ribi {

struct QtStdVectorFunctionModel: public QAbstractTableModel
{
  ///The variable is the variable used in a function
  ///Examples:
  /// - in the equation 'y = 3 * x', the variable used is 'x'
  /// - in the equation 'u = cos(t)', the variable used is 't'
  explicit QtStdVectorFunctionModel(
    const std::string& variable,
    QObject *parent = 0) noexcept;

  ///Working with the raw data
  const std::vector<std::string>& GetRawData() const noexcept { return m_data; }

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Working with the raw data
  void SetRawData(const std::vector<std::string>& data);

  ///Set the header text
  void SetHeaderData(const std::string& title, const std::vector<std::string>& header_text);

  private:
  ///The raw data
  std::vector<std::string> m_data;

  ///The horizontal header text (for the only one column)
  std::string m_header_horizontal_text;

  ///The vertical header text
  std::vector<std::string> m_header_vertical_text;

  ///The variable is the variable used in a function
  ///Examples:
  /// - in the equation 'y = 3 * x', the variable used is 'x'
  /// - in the equation 'u = cos(t)', the variable used is 't'
  const std::string m_variable;

  ///Must be defined from ABC
  int columnCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Must be defined from ABC
  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

  ///These flags are needed to allow editing
  Qt::ItemFlags flags(const QModelIndex &index) const noexcept;

  ///Redefined from ABC
  QVariant headerData(int section, Qt::Orientation orientation, int role) const;

  ///Redefined from ABC
  bool insertRows(int row, int count, const QModelIndex &parent);

  ///Redefined from ABC
  bool removeRows(int row, int count, const QModelIndex &parent);

  ///Must be defined from ABC
  int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Needed for editable data
  bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);

  ///Redefined from ABC
  bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);
};

} //~namespace ribi

#endif // QTSTDVECTORFUNCTIONMODEL_H

 

 

 

 

 

./CppQtModel/qtstdvectorfunctionmodel.cpp

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "qtstdvectorfunctionmodel.h"

#include <cassert>
#include <boost/lexical_cast.hpp>

#include "modelfunctionparser.h"

#include "trace.h"

#pragma GCC diagnostic pop

ribi::QtStdVectorFunctionModel::QtStdVectorFunctionModel(
  const std::string& variable,
  QObject *parent) noexcept
  : QAbstractTableModel(parent),
    m_data{},
    m_header_horizontal_text{},
    m_header_vertical_text{},
    m_variable{variable}
{
  assert(!variable.empty());
}

int ribi::QtStdVectorFunctionModel::columnCount(const QModelIndex &) const noexcept
{
  return rowCount() > 0 ? 1 : 0;
}

QVariant ribi::QtStdVectorFunctionModel::data(const QModelIndex &index, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole &&  role != Qt::DisplayRole) return QVariant();

  assert(index.isValid());

  const int row = index.row();
  #ifndef NDEBUG
  const int col = index.column();
  assert(row >= 0);
  assert(row < this->rowCount());
  assert(row < boost::numeric_cast<int>(m_data.size()));
  assert(col == 0);
  #endif

  return QString(m_data[row].c_str());
}

Qt::ItemFlags ribi::QtStdVectorFunctionModel::flags(const QModelIndex &) const noexcept
{
  return
    Qt::ItemIsSelectable
  | Qt::ItemIsEditable
  | Qt::ItemIsDragEnabled
  | Qt::ItemIsDropEnabled
  | Qt::ItemIsEnabled;
}

std::string ribi::QtStdVectorFunctionModel::GetVersion() noexcept
{
  return "1.2";
}

std::vector<std::string> ribi::QtStdVectorFunctionModel::GetVersionHistory() noexcept
{
  return {
    "2013-05-15: version 1.0: initial version",
    "2013-05-28: version 1.1: allow columnCount to be zero, if rowCount is zero",
    "2013-07-05: version 1.2: signal layoutChanged emitted correctly"
  };
}

QVariant ribi::QtStdVectorFunctionModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();

  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    return QString( m_header_horizontal_text.c_str() );
  }
  else
  {
    assert(orientation == Qt::Vertical);
    //No idea why this happens
    if (!(section < boost::numeric_cast<int>(m_header_vertical_text.size())))
    {
      return QVariant();
    }

    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    return QString( m_header_vertical_text[section].c_str() );
  }
}

bool ribi::QtStdVectorFunctionModel::insertRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginInsertRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() + count;
  m_data.resize(new_size);
  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endInsertRows();

  //It worked!
  return true;
}

bool ribi::QtStdVectorFunctionModel::removeRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginRemoveRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() - count;
  m_data.resize(new_size);
  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endRemoveRows();

  //It worked!
  return true;
}

int ribi::QtStdVectorFunctionModel::rowCount(const QModelIndex &) const noexcept
{
  return boost::numeric_cast<int>(m_data.size());
}

bool ribi::QtStdVectorFunctionModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
  const int row = index.row();
  #ifndef NDEBUG
  const int col = index.column();
  assert(col == 0);
  #endif

  assert(row < boost::numeric_cast<int>(m_data.size()));

  //Check if it is a valid function
  const std::string my_function = value.toString().toStdString();
  try
  {
    ModelFunctionParser f(my_function,m_variable);
  }
  catch (std::runtime_error&)
  {
    return false;
  }

  //Store the correctly formed function
  m_data[row] = my_function;

  ///This line below is needed to let multiple views synchronize
  emit dataChanged(index,index);

  //Editing succeeded!
  return true;
}

void ribi::QtStdVectorFunctionModel::SetHeaderData(
  const std::string& horizontal_header_text, const std::vector<std::string>& vertical_header_text)
{
  if (m_header_horizontal_text != horizontal_header_text)
  {
    emit layoutAboutToBeChanged();
    assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
    m_header_horizontal_text = horizontal_header_text;
    emit layoutChanged();
    emit headerDataChanged(Qt::Horizontal,0,1);
  }

  if (m_header_vertical_text != vertical_header_text)
  {
    emit layoutAboutToBeChanged();
    const int new_size = boost::numeric_cast<int>(vertical_header_text.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }

    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_header_vertical_text = vertical_header_text;

    assert(this->rowCount() == boost::numeric_cast<int>(vertical_header_text.size())
      && "So emit layoutChange can work on the newest layout");

    emit layoutChanged();
    emit headerDataChanged(Qt::Vertical,0,new_size);
  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
}

bool ribi::QtStdVectorFunctionModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int /*role*/)
{
  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    assert(m_header_horizontal_text != value.toString().toStdString());
    m_header_horizontal_text = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
  else
  {
    assert(orientation == Qt::Vertical);
    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    assert(m_header_vertical_text[section] != value.toString().toStdString());
    m_header_vertical_text[section] = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
}

void ribi::QtStdVectorFunctionModel::SetRawData(const std::vector<std::string>& data)
{
  if (m_data != data)
  {
    emit layoutAboutToBeChanged();
    const int new_size = boost::numeric_cast<int>(data.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }
    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_data = data;

    assert(this->rowCount() == boost::numeric_cast<int>(data.size())
      && "So emit layoutChange can work on the newest layout");

    emit layoutChanged();

    const QModelIndex top_left = this->index(0,0);
    const QModelIndex bottom_right = this->index(m_data.size() - 1, 1);
    emit dataChanged(top_left,bottom_right);

  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
}

 

 

 

 

 

./CppQtModel/qtstdvectorstringmodel.h

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#ifndef QTSTDVECTORSTRINGMODEL_H
#define QTSTDVECTORSTRINGMODEL_H

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/numeric/ublas/vector.hpp>
#include <QAbstractTableModel>
#pragma GCC diagnostic pop

namespace ribi {

struct QtStdVectorStringModel: public QAbstractTableModel
{
  explicit QtStdVectorStringModel(QObject *parent = 0) noexcept;

  ///Reading the raw data
  const std::vector<std::string>& GetRawData() const noexcept { return m_data; }

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Writing the raw data
  void SetRawData(const std::vector<std::string>& data);

  ///Set the header text
  void SetHeaderData(const std::string& title, const std::vector<std::string>& header_text);

  private:
  ///The raw data
  std::vector<std::string> m_data;

  ///The horizontal header text (for the only one column)
  std::string m_header_horizontal_text;

  ///The vertical header text
  std::vector<std::string> m_header_vertical_text;

  ///Must be defined from ABC
  int columnCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Must be defined from ABC
  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

  ///These flags are needed to allow editing
  Qt::ItemFlags flags(const QModelIndex &index) const noexcept;

  ///Redefined from ABC
  QVariant headerData(int section, Qt::Orientation orientation, int role) const;

  ///Redefined from ABC
  bool insertRows(int row, int count, const QModelIndex &parent);

  ///Redefined from ABC
  bool removeRows(int row, int count, const QModelIndex &parent);

  ///Must be defined from ABC
  int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Needed for editable data
  bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);

  ///Redefined from ABC
  bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);
};

} //~namespace ribi

#endif // QTSTDVECTORSTRINGMODEL_H

 

 

 

 

 

./CppQtModel/qtstdvectorstringmodel.cpp

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "qtstdvectorstringmodel.h"

#include <cassert>
#include <boost/lexical_cast.hpp>

#include "trace.h"

#pragma GCC diagnostic pop

ribi::QtStdVectorStringModel::QtStdVectorStringModel(QObject *parent) noexcept
  : QAbstractTableModel(parent),
    m_data{},
    m_header_horizontal_text{},
    m_header_vertical_text{}
{

}

int ribi::QtStdVectorStringModel::columnCount(const QModelIndex &) const noexcept
{
  return rowCount() > 0 ? 1 : 0;
}

QVariant ribi::QtStdVectorStringModel::data(const QModelIndex &index, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole &&  role != Qt::DisplayRole) return QVariant();

  assert(index.isValid());

  const int row = index.row();

  #ifndef NDEBUG
  const int col = index.column();
  assert(row >= 0);
  assert(row < this->rowCount());
  assert(row < boost::numeric_cast<int>(m_data.size()));
  assert(col == 0);
  #endif

  return QString(m_data[row].c_str());
}

Qt::ItemFlags ribi::QtStdVectorStringModel::flags(const QModelIndex &) const noexcept
{
  return
    Qt::ItemIsSelectable
  | Qt::ItemIsEditable
  | Qt::ItemIsDragEnabled
  | Qt::ItemIsDropEnabled
  | Qt::ItemIsEnabled;
}

std::string ribi::QtStdVectorStringModel::GetVersion() noexcept
{
  return "1.2";
}

std::vector<std::string> ribi::QtStdVectorStringModel::GetVersionHistory() noexcept
{
  return {
    "2013-05-15: version 1.0: initial version",
    "2013-05-28: version 1.1: allow columnCount to be zero, if rowCount is zero",
    "2013-07-05: version 1.2: signal layoutChanged emitted correctly"
  };
}

QVariant ribi::QtStdVectorStringModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();

  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    return QString( m_header_horizontal_text.c_str() );
  }
  else
  {
    assert(orientation == Qt::Vertical);
    //No idea why this happens
    if (!(section < boost::numeric_cast<int>(m_header_vertical_text.size())))
    {
      return QVariant();
    }

    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    return QString( m_header_vertical_text[section].c_str() );
  }
}

bool ribi::QtStdVectorStringModel::insertRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginInsertRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() + count;
  m_data.resize(new_size);
  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endInsertRows();

  //It worked!
  return true;
}

bool ribi::QtStdVectorStringModel::removeRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginRemoveRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() - count;
  m_data.resize(new_size);
  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endRemoveRows();

  //It worked!
  return true;
}

int ribi::QtStdVectorStringModel::rowCount(const QModelIndex &) const noexcept
{
  return boost::numeric_cast<int>(m_data.size());
}

bool ribi::QtStdVectorStringModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
  const int row = index.row();

  #ifndef NDEBUG
  const int col = index.column();
  assert(row < boost::numeric_cast<int>(m_data.size()));
  assert(col == 0);
  #endif

  m_data[row] = value.toString().toStdString();

  ///This line below is needed to let multiple views synchronize
  emit dataChanged(index,index);

  //Editing succeeded!
  return true;
}

void ribi::QtStdVectorStringModel::SetHeaderData(
  const std::string& horizontal_header_text, const std::vector<std::string>& vertical_header_text)
{
  if (m_header_horizontal_text != horizontal_header_text)
  {
    emit layoutAboutToBeChanged();
    assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
    m_header_horizontal_text = horizontal_header_text;
    emit layoutChanged();
    emit headerDataChanged(Qt::Horizontal,0,1);
  }

  if (m_header_vertical_text != vertical_header_text)
  {
    emit layoutAboutToBeChanged();
    const int new_size = boost::numeric_cast<int>(vertical_header_text.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }
    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_header_vertical_text = vertical_header_text;

    assert(this->rowCount() == boost::numeric_cast<int>(vertical_header_text.size())
      && "So emit layoutChange can work on the newest layout");

    emit layoutChanged();

    emit headerDataChanged(Qt::Vertical,0,new_size);
  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
}

bool ribi::QtStdVectorStringModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int /*role*/)
{
  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    assert(m_header_horizontal_text != value.toString().toStdString());
    m_header_horizontal_text = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
  else
  {
    assert(orientation == Qt::Vertical);
    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    assert(m_header_vertical_text[section] != value.toString().toStdString());
    m_header_vertical_text[section] = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
}


void ribi::QtStdVectorStringModel::SetRawData(const std::vector<std::string>& data)
{
  if (m_data != data)
  {
    const int new_size = boost::numeric_cast<int>(data.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());

    }

    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_data = data;

    assert(this->rowCount() == boost::numeric_cast<int>(data.size())
      && "So emit layoutChange can work on the newest layout");

    emit layoutChanged();

    const QModelIndex top_left = this->index(0,0);
    const QModelIndex bottom_right = this->index(m_data.size() - 1, 1);
    emit dataChanged(top_left,bottom_right);

  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
}

 

 

 

 

 

./CppQtModel/qtublasmatrixdoublemodel.h

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#ifndef QTMATRIXDOUBLEMODEL_H
#define QTMATRIXDOUBLEMODEL_H



#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/numeric/ublas/matrix.hpp>
#include <QAbstractTableModel>
#pragma GCC diagnostic pop

namespace ribi {

struct QtUblasMatrixDoubleModel : public QAbstractTableModel
{
  explicit QtUblasMatrixDoubleModel(QObject *parent = 0) noexcept;

  ///Obtain the raw data
  const boost::numeric::ublas::matrix<double>& GetRawData() const  noexcept{ return m_data; }

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Write the raw data
  void SetRawData(const boost::numeric::ublas::matrix<double>& data);

  ///Set the header text
  void SetHeaderData(const std::vector<std::string>& horizontal_header_text, const std::vector<std::string>& vertical_header_text);

  private:
  ///The raw data
  boost::numeric::ublas::matrix<double> m_data;

  ///The horizontal header text
  std::vector<std::string> m_header_horizontal_text;

  ///The vertical header text
  std::vector<std::string> m_header_vertical_text;

  ///Must be defined from ABC
  int columnCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  //Must be defined from ABC
  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

  ///These flags are needed to allow editing
  Qt::ItemFlags flags(const QModelIndex &index) const noexcept;

  ///Redefined from ABC
  QVariant headerData(int section, Qt::Orientation orientation, int role) const;

  ///Redefined from ABC
  bool insertColumns(int row, int count, const QModelIndex &parent);

  ///Redefined from ABC
  bool insertRows(int row, int count, const QModelIndex &parent);

  ///Redefined from ABC
  bool removeColumns(int row, int count, const QModelIndex &parent);

  ///Redefined from ABC
  bool removeRows(int row, int count, const QModelIndex &parent);

  ///Must be defined from ABC
  int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Needed for editable data
  bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);

  ///Redefined from ABC
  bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);
};

} //~namespace ribi

#endif // QTMATRIXDOUBLEMODEL_H

 

 

 

 

 

./CppQtModel/qtublasmatrixdoublemodel.cpp

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "qtublasmatrixdoublemodel.h"

#include <numeric>
#include <cassert>
#include <boost/lexical_cast.hpp>

#include "matrix.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::QtUblasMatrixDoubleModel::QtUblasMatrixDoubleModel(QObject *parent) noexcept
  : QAbstractTableModel(parent),
    m_data{},
    m_header_horizontal_text{},
    m_header_vertical_text{}
{

}

int ribi::QtUblasMatrixDoubleModel::columnCount(const QModelIndex &) const noexcept
{
  return m_data.size2();
}

QVariant ribi::QtUblasMatrixDoubleModel::data(const QModelIndex &index, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();

  assert(index.isValid());

  const int row = index.row();
  const int col = index.column();
  assert(row < boost::numeric_cast<int>(m_data.size1()));
  assert(col < boost::numeric_cast<int>(m_data.size2()));

  #ifdef RETURN_DOUBLE_723465978463059835
  return m_data(row,col);
  #else
  //Convert to string, otherwise the number digits behind the comma
  //will be set to 2, e.g. 0.01
  const std::string s = boost::lexical_cast<std::string>(m_data(row,col));
  return QString(s.c_str());
  #endif
}

Qt::ItemFlags ribi::QtUblasMatrixDoubleModel::flags(const QModelIndex &) const noexcept
{
  return
    Qt::ItemIsSelectable
  | Qt::ItemIsEditable
  | Qt::ItemIsDragEnabled
  | Qt::ItemIsDropEnabled
  | Qt::ItemIsEnabled;
}

std::string ribi::QtUblasMatrixDoubleModel::GetVersion() noexcept
{
  return "1.3";
}

std::vector<std::string> ribi::QtUblasMatrixDoubleModel::GetVersionHistory() noexcept
{
  return {
    "2013-05-15: version 1.0: initial version",
    "2013-05-21: version 1.1: added columns and rows are initialized with zeroes",
    "2013-05-23: version 1.2: allow an infine amount of digits behind the comma",
    "2013-07-05: version 1.3: signal layoutChanged emitted correctly"
  };
}

QVariant ribi::QtUblasMatrixDoubleModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();

  if (orientation == Qt::Horizontal)
  {
    //No idea why this happens
    if (!(section < boost::numeric_cast<int>(m_header_horizontal_text.size())))
    {
      return QVariant();
    }

    assert(section < boost::numeric_cast<int>(m_header_horizontal_text.size()));
    return QString( m_header_horizontal_text[section].c_str() );
  }
  else
  {
    assert(orientation == Qt::Vertical);
    //No idea why this happens
    if (!(section < boost::numeric_cast<int>(m_header_vertical_text.size())))
    {
      return QVariant();
    }

    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    return QString( m_header_vertical_text[section].c_str() );
  }
}

bool ribi::QtUblasMatrixDoubleModel::insertColumns(int col, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginInsertColumns(parent,col,col+count-1);

  //The real operation: resize the m_data
  assert(m_data.size2() == m_header_horizontal_text.size());

  const int new_size = m_data.size2() + count;

  boost::numeric::ublas::matrix<double> new_data = boost::numeric::ublas::zero_matrix<double>(m_data.size1(),new_size);
  const int n_rows = boost::numeric_cast<int>(m_data.size1());
  const int n_cols = boost::numeric_cast<int>(m_data.size2());
  for (int y = 0; y != n_rows; ++y)
  {
    for (int x = 0; x != n_cols; ++x)
    {
      assert(y < boost::numeric_cast<int>(new_data.size1()));
      assert(x < boost::numeric_cast<int>(new_data.size2()));
      assert(y < boost::numeric_cast<int>(m_data.size1()));
      assert(x < boost::numeric_cast<int>(m_data.size2()));
      new_data(y,x) = m_data(y,x);
    }
  }
  m_data = new_data;

  m_header_horizontal_text.resize(new_size,"");

  assert(m_data.size2() == m_header_horizontal_text.size());

  //Must be called in the end
  this->endInsertColumns();

  //It worked!
  return true;
}

bool ribi::QtUblasMatrixDoubleModel::insertRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginInsertRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size1() == m_header_vertical_text.size());

  const int new_size = m_data.size1() + count;

  boost::numeric::ublas::matrix<double> new_data = boost::numeric::ublas::zero_matrix<double>(new_size,m_data.size2());
  const int n_rows = boost::numeric_cast<int>(m_data.size1());
  const int n_cols = boost::numeric_cast<int>(m_data.size2());
  for (int y = 0; y != n_rows; ++y)
  {
    for (int x = 0; x != n_cols; ++x)
    {
      assert(y < boost::numeric_cast<int>(new_data.size1()));
      assert(x < boost::numeric_cast<int>(new_data.size2()));
      assert(y < boost::numeric_cast<int>(m_data.size1()));
      assert(x < boost::numeric_cast<int>(m_data.size2()));
      new_data(y,x) = m_data(y,x);
    }
  }
  m_data = new_data;

  m_header_vertical_text.resize(new_size);

  assert(m_data.size1() == m_header_vertical_text.size());

  //Must be called in the end
  this->endInsertRows();

  //It worked!
  return true;
}

bool ribi::QtUblasMatrixDoubleModel::removeColumns(int col, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginRemoveColumns(parent,col,col+count-1);

  //The real operation: resize the m_data
  assert(m_data.size2() == m_header_horizontal_text.size());

  const int new_size = m_data.size2() - count;
  m_data.resize(m_data.size1(),new_size);
  m_header_horizontal_text.resize(new_size);

  assert(m_data.size2() == m_header_horizontal_text.size());

  //Must be called in the end
  this->endRemoveColumns();

  //It worked!
  return true;
}

bool ribi::QtUblasMatrixDoubleModel::removeRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginRemoveRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size1() == m_header_vertical_text.size());

  const int new_size = m_data.size1() - count;
  m_data.resize(new_size,m_data.size2());
  m_header_vertical_text.resize(new_size);

  assert(m_data.size1() == m_header_vertical_text.size());

  //Must be called in the end
  this->endRemoveRows();

  //It worked!
  return true;
}

int ribi::QtUblasMatrixDoubleModel::rowCount(const QModelIndex &) const noexcept
{
  return m_data.size1();
}

bool ribi::QtUblasMatrixDoubleModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
  const int row = index.row();
  const int col = index.column();
  assert(row < boost::numeric_cast<int>(m_data.size1()));
  assert(col < boost::numeric_cast<int>(m_data.size2()));
  const QString s = value.toString();
  m_data(row,col) = s.toDouble();
  ///This line below is needed to let multiple views synchronize
  emit dataChanged(index,index);

  //Editing succeeded!
  return true;
}

void ribi::QtUblasMatrixDoubleModel::SetHeaderData(
  const std::vector<std::string>& horizontal_header_text,
  const std::vector<std::string>& vertical_header_text)
{

  if (m_header_horizontal_text != horizontal_header_text)
  {
    emit layoutAboutToBeChanged();
    const int new_size = boost::numeric_cast<int>(horizontal_header_text.size());
    const int cur_size = this->columnCount();
    if (cur_size < new_size)
    {
      this->insertColumns(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeColumns(cur_size,cur_size - new_size,QModelIndex());
    }

    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_header_horizontal_text = horizontal_header_text;

    assert(this->columnCount() == boost::numeric_cast<int>(horizontal_header_text.size()));

    //If you forget this line, the view displays a different number of rows than m_data has
    emit layoutChanged();

    emit headerDataChanged(Qt::Horizontal,0,new_size);

  }

  if (m_header_vertical_text != vertical_header_text)
  {
    emit layoutAboutToBeChanged();
    const int new_size = boost::numeric_cast<int>(vertical_header_text.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }

    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_header_vertical_text = vertical_header_text;

    assert(this->rowCount() == boost::numeric_cast<int>(vertical_header_text.size()));

    //If you forget this line, the view displays a different number of rows than m_data has
    emit layoutChanged();

    emit headerDataChanged(Qt::Vertical,0,new_size);
  }

  assert(this->columnCount() == boost::numeric_cast<int>(this->m_data.size2()));
  assert(this->columnCount() == boost::numeric_cast<int>(m_header_horizontal_text.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size1()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
}

bool ribi::QtUblasMatrixDoubleModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int /*role*/)
{
  if (orientation == Qt::Horizontal)
  {
    assert(section < boost::numeric_cast<int>(m_header_horizontal_text.size()));
    assert(m_header_horizontal_text[section] != value.toString().toStdString());
    m_header_horizontal_text[section] = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
  else
  {
    assert(orientation == Qt::Vertical);
    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    assert(m_header_vertical_text[section] != value.toString().toStdString());
    m_header_vertical_text[section] = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
}

void ribi::QtUblasMatrixDoubleModel::SetRawData(const boost::numeric::ublas::matrix<double>& data)
{
  if (!Matrix::MatricesAreEqual(m_data,data))
  {
    emit layoutAboutToBeChanged();
    //Check for row count
    const int new_row_count = boost::numeric_cast<int>(data.size1());
    const int cur_row_count = this->rowCount();
    {
      if (cur_row_count < new_row_count)
      {
        this->insertRows(cur_row_count,new_row_count - cur_row_count,QModelIndex());
      }
      else if (cur_row_count > new_row_count)
      {
        this->removeRows(cur_row_count,cur_row_count - new_row_count,QModelIndex());
      }
    }
    //Check for column count
    const int new_col_count = boost::numeric_cast<int>(data.size2());
    const int cur_col_count = this->columnCount();
    {
      if (cur_col_count < new_col_count)
      {
        this->insertColumns(cur_col_count,new_col_count - cur_col_count,QModelIndex());
      }
      else if (cur_col_count > new_col_count)
      {
        this->removeColumns(cur_col_count,cur_col_count - new_col_count,QModelIndex());
      }
    }
    assert(m_data.size1() == data.size1());
    assert(m_data.size2() == data.size2());

    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_data = data;

    assert(Matrix::MatricesAreEqual(m_data,data));

    assert(this->rowCount() == boost::numeric_cast<int>(data.size1())
      && "So emit layoutChange can work on the newest layout");
    assert(this->columnCount() == boost::numeric_cast<int>(data.size2())
      && "So emit layoutChange can work on the newest layout");

    //If you forget this line, the view displays a different number of rows than m_data has
    emit layoutChanged();

    const QModelIndex top_left = this->index(0,0);
    const QModelIndex bottom_right = this->index(m_data.size1() - 1, m_data.size2() - 1);
    emit dataChanged(top_left,bottom_right);
  }

  //Check MatricesAreEqual
  #ifndef NDEBUG
  assert(m_data.size1() == data.size1());
  assert(m_data.size2() == data.size2());
  const std::size_t n_rows = m_data.size1();
  const std::size_t n_cols = m_data.size2();
  for (std::size_t row = 0; row != n_rows; ++row)
  {
    for (std::size_t col = 0; col != n_cols; ++col)
    {
      assert(m_data(row,col) == data(row,col));
    }
  }

  assert(this->columnCount() == boost::numeric_cast<int>(this->m_data.size2()));
  assert(this->columnCount() == boost::numeric_cast<int>(m_header_horizontal_text.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size1()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(Matrix::MatricesAreEqual(m_data,data));
  #endif
}

 

 

 

 

 

./CppQtModel/qtublasvectordoublemodel.h

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#ifndef QTUBLASVECTORDOUBLEMODEL_H
#define QTUBLASVECTORDOUBLEMODEL_H

#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/numeric/ublas/vector.hpp>
#include <QAbstractTableModel>
#pragma GCC diagnostic pop

namespace ribi {

struct QtUblasVectorDoubleModel: public QAbstractTableModel
{
  explicit QtUblasVectorDoubleModel(QObject *parent = 0) noexcept;

  ///Working with the raw data
  const boost::numeric::ublas::vector<double>& GetRawData() const noexcept{ return m_data; }

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Set the header text
  void SetHeaderData(const std::string& title, const std::vector<std::string>& header_text);

  ///Working with the raw data
  void SetRawData(const boost::numeric::ublas::vector<double>& data);

  private:
  ///The raw data
  boost::numeric::ublas::vector<double> m_data;

  ///The horizontal header text (for the only one column)
  std::string m_header_horizontal_text;

  ///The vertical header text
  std::vector<std::string> m_header_vertical_text;

  ///Must be defined from ABC
  int columnCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Must be defined from ABC
  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

  ///These flags are needed to allow editing
  Qt::ItemFlags flags(const QModelIndex &index) const noexcept;

  ///Redefined from ABC
  QVariant headerData(int section, Qt::Orientation orientation, int role) const;

  ///Redefined from ABC
  //bool insertColumns(int column, int count, const QModelIndex &parent) = delete;

  ///Redefined from ABC
  bool insertRows(int row, int count, const QModelIndex &parent);

  ///Redefined from ABC
  bool removeRows(int row, int count, const QModelIndex &parent);

  ///Must be defined from ABC
  int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Needed for editable data
  bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);

  ///Redefined from ABC
  bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);

  #ifndef NDEBUG
  ///Test this class
  static void Test() noexcept;
  #endif

};

} //~namespace ribi

#endif // QTUBLASVECTORDOUBLEMODEL_H

 

 

 

 

 

./CppQtModel/qtublasvectordoublemodel.cpp

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "qtublasvectordoublemodel.h"

#include <cassert>
#include <boost/lexical_cast.hpp>
#include "matrix.h"
#include "testtimer.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::QtUblasVectorDoubleModel::QtUblasVectorDoubleModel(QObject *parent) noexcept
  : QAbstractTableModel(parent),
    m_data{},
    m_header_horizontal_text{},
    m_header_vertical_text{}
{
  #ifndef NDEBUG
  Test();
  #endif
}

int ribi::QtUblasVectorDoubleModel::columnCount(const QModelIndex &) const noexcept
{
  return rowCount() > 0 ? 1 : 0;
}

QVariant ribi::QtUblasVectorDoubleModel::data(const QModelIndex &index, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();

  assert(index.isValid());

  const int row = index.row();

  #ifndef NDEBUG
  const int col = index.column();
  assert(row >= 0);
  assert(row < this->rowCount());
  assert(row < boost::numeric_cast<int>(m_data.size()));
  assert(col == 0);
  #endif

  #ifdef RETURN_DOUBLE_723465978463059835
  return m_data(row);
  #else
  //Convert to string, otherwise the number digits behind the comma
  //will be set to 2, e.g. 0.01
  const std::string s = boost::lexical_cast<std::string>(m_data(row));
  return QString(s.c_str());
  #endif

}

Qt::ItemFlags ribi::QtUblasVectorDoubleModel::flags(const QModelIndex &) const noexcept
{
  return
    Qt::ItemIsSelectable
  | Qt::ItemIsEditable
  | Qt::ItemIsDragEnabled
  | Qt::ItemIsDropEnabled
  | Qt::ItemIsEnabled;
}

std::string ribi::QtUblasVectorDoubleModel::GetVersion() noexcept
{
  return "1.5";
}

std::vector<std::string> ribi::QtUblasVectorDoubleModel::GetVersionHistory() noexcept
{
  return {
    "2013-05-15: version 1.0: initial version",
    "2013-05-21: version 1.1: added rows are initialized with zeroes",
    "2013-05-23: version 1.2: allow an infine amount of digits behind the comma",
    "2013-05-28: version 1.3: allow columnCount to be zero, if rowCount is zero",
    "2013-06-27: version 1.4: added self-test",
    "2013-07-05: version 1.5: signal layoutChanged emitted correctly"
  };
}

QVariant ribi::QtUblasVectorDoubleModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();

  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    return QString( m_header_horizontal_text.c_str() );
  }
  else
  {
    assert(orientation == Qt::Vertical);
    //No idea why this happens
    if (!(section < boost::numeric_cast<int>(m_header_vertical_text.size())))
    {
      return QVariant();
    }

    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    return QString( m_header_vertical_text[section].c_str() );
  }
}

bool ribi::QtUblasVectorDoubleModel::insertRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginInsertRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() + count;

  boost::numeric::ublas::vector<double> new_data = boost::numeric::ublas::zero_vector<double>(new_size);
  std::copy(m_data.begin(),m_data.end(),new_data.begin());
  m_data = new_data;

  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endInsertRows();

  //It worked!
  return true;
}

bool ribi::QtUblasVectorDoubleModel::removeRows(int row, int count, const QModelIndex &parent)
{
  //Must be called before the real operation
  this->beginRemoveRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() - count;
  m_data.resize(new_size);
  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endRemoveRows();

  //It worked!
  return true;
}

int ribi::QtUblasVectorDoubleModel::rowCount(const QModelIndex &) const noexcept
{
  assert(m_data.size() == m_header_vertical_text.size());
  return boost::numeric_cast<int>(m_data.size());
}


bool ribi::QtUblasVectorDoubleModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
  const int row = index.row();

  #ifndef NDEBUG
  const int col = index.column();
  assert(row < boost::numeric_cast<int>(m_data.size()));
  assert(col == 0);
  #endif

  m_data(row) = value.toDouble();

  ///This line below is needed to let multiple views synchronize
  emit dataChanged(index,index);

  //Editing succeeded!
  return true;
}

bool ribi::QtUblasVectorDoubleModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int /*role*/)
{
  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    assert(m_header_horizontal_text != value.toString().toStdString());
    m_header_horizontal_text = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
  else
  {
    assert(orientation == Qt::Vertical);
    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    assert(m_header_vertical_text[section] != value.toString().toStdString());
    m_header_vertical_text[section] = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);
    //Editing succeeded!
    return true;
  }
}

void ribi::QtUblasVectorDoubleModel::SetHeaderData(
  const std::string& horizontal_header_text, const std::vector<std::string>& vertical_header_text)
{
  if (m_header_horizontal_text != horizontal_header_text)
  {
    emit layoutAboutToBeChanged();
    assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
    m_header_horizontal_text = horizontal_header_text;
    emit layoutChanged();
    emit headerDataChanged(Qt::Horizontal,0,1);
  }

  if (m_header_vertical_text != vertical_header_text)
  {
    emit layoutAboutToBeChanged();
    const int new_size = boost::numeric_cast<int>(vertical_header_text.size());
    const int cur_size = this->rowCount();

    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }

    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_header_vertical_text = vertical_header_text;

    assert(this->rowCount() == boost::numeric_cast<int>(vertical_header_text.size())
      && "So emit layoutChange can work on the newest layout");

    emit layoutChanged();
    emit headerDataChanged(Qt::Vertical,0,new_size);
  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
}

void ribi::QtUblasVectorDoubleModel::SetRawData(const boost::numeric::ublas::vector<double>& data)
{
  if (!Matrix::VectorsDoubleAreEqual(m_data,data))
  {
    const int new_size = boost::numeric_cast<int>(data.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }
    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_data = data;

    assert(this->rowCount() == boost::numeric_cast<int>(data.size())
      && "So emit layoutChange can work on the newest layout");

    //If you forget this line, the view displays a different number of rows than m_data has
    emit layoutChanged();

    //emit dataChanged(QModelIndex(),QModelIndex());
    const QModelIndex top_left = this->index(0,0);
    const QModelIndex bottom_right = this->index(m_data.size() - 1, 0);
    emit dataChanged(top_left,bottom_right);
  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
}

#ifndef NDEBUG
void ribi::QtUblasVectorDoubleModel::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  const TestTimer test_timer(__func__,__FILE__,1.0);
  {
    QtUblasVectorDoubleModel model;
    assert(model.rowCount()    == 0);
    assert(model.columnCount() == 0);
    model.insertRow(1);
    assert(model.rowCount()    == 1);
    assert(model.columnCount() == 1);
    model.removeRow(1);
    assert(model.rowCount()    == 0);
    assert(model.columnCount() == 0);
  }
}
#endif

 

 

 

 

 

./CppQtModel/qtublasvectorintmodel.h

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#ifndef QTUBLASVECTORINTMODEL_H
#define QTUBLASVECTORINTMODEL_H

#include <string>
#include <vector>

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include <boost/numeric/ublas/vector.hpp>
#include <QAbstractTableModel>
#pragma GCC diagnostic pop

namespace ribi {

struct QtUblasVectorIntModel: public QAbstractTableModel
{
  ///Create a QtUblasVectorIntModel
  ///with range
  ///- from = std::numeric_limits<int>::min()
  ///- to   = std::numeric_limits<int>::max()
  explicit QtUblasVectorIntModel(QObject *parent = 0) noexcept;

  ///Working with the raw data
  const boost::numeric::ublas::vector<int>& GetRawData() const  noexcept{ return m_data; }

  ///Obtain the version of this class
  static std::string GetVersion() noexcept;

  ///Obtain the version history of this class
  static std::vector<std::string> GetVersionHistory() noexcept;

  ///Set the range the values must remain in
  ///The range includes from, excludes to
  void SetRange(const int from, const int to, const int default_value);

  ///Working with the raw data
  void SetRawData(const boost::numeric::ublas::vector<int>& data);

  ///Set the header text
  void SetHeaderData(const std::string& title, const std::vector<std::string>& header_text);

  private:
  ///The raw data
  boost::numeric::ublas::vector<int> m_data;

  ///The horizontal header text (for the only one column)
  std::string m_header_horizontal_text;

  ///The vertical header text
  std::vector<std::string> m_header_vertical_text;

  ///The value set for default
  int m_range_default;

  ///The maximum value all values must have
  int m_range_max;

  ///The minimum value all values must have
  int m_range_min;

  ///Must be defined from ABC
  int columnCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Must be defined from ABC
  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

  ///These flags are needed to allow editing
  Qt::ItemFlags flags(const QModelIndex &index) const noexcept;

  ///Redefined from ABC
  QVariant headerData(int section, Qt::Orientation orientation, int role) const;

  ///Redefined from ABC
  bool insertRows(int row, int count, const QModelIndex &parent);

  ///Check if the class is in a valid state
  #ifndef NDEBUG
  bool IsValid() const noexcept;
  #endif

  ///Redefined from ABC
  bool removeRows(int row, int count, const QModelIndex &parent);

  ///Must be defined from ABC
  int rowCount(const QModelIndex &parent = QModelIndex()) const noexcept;

  ///Needed for editable data
  bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);

  ///Redefined from ABC
  bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role);

  #ifndef NDEBUG
  static void Test() noexcept;
  #endif

};

} //~namespace ribi

#endif // QTUBLASVECTORINTMODEL_H

 

 

 

 

 

./CppQtModel/qtublasvectorintmodel.cpp

 

//---------------------------------------------------------------------------
/*
QtModel, my classes derived from QAbstractTableModel
Copyright (C) 2013-2015 Richel Bilderbeek

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
//From http://www.richelbilderbeek.nl/CppQtModel.htm
//---------------------------------------------------------------------------
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Weffc++"
#pragma GCC diagnostic ignored "-Wunused-local-typedefs"
#include "qtublasvectorintmodel.h"

#include <cassert>
#include <numeric>
#include <boost/lexical_cast.hpp>
#include <boost/numeric/ublas/vector.hpp>
#include "matrix.h"
#include "trace.h"

#pragma GCC diagnostic pop

ribi::QtUblasVectorIntModel::QtUblasVectorIntModel(QObject *parent) noexcept
  : QAbstractTableModel(parent),
    m_data{},
    m_header_horizontal_text{},
    m_header_vertical_text{},
    m_range_default{0},
    m_range_max{std::numeric_limits<int>::max()},
    m_range_min{std::numeric_limits<int>::min()}
{
  #ifndef NDEBUG
  Test();
  #endif

  assert(m_range_min < m_range_max);
  assert(this->IsValid());
}

int ribi::QtUblasVectorIntModel::columnCount(const QModelIndex &) const noexcept
{
  return rowCount() > 0 ? 1 : 0;
}

QVariant ribi::QtUblasVectorIntModel::data(const QModelIndex &index, int role) const
{
  assert(this->IsValid());

  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();



  assert(index.isValid());

  const int row = index.row();

  #ifndef NDEBUG
  const int col = index.column();
  assert(row >= 0);
  assert(row < this->rowCount());
  assert(row < boost::numeric_cast<int>(m_data.size()));
  assert(col == 0);
  if (m_data(row) < m_range_min || m_data(row) >= m_range_max)
  {
    TRACE("BREAK");
  }

  assert(m_data(row) >= m_range_min && "One of the purposes of this class it to ensure this");
  assert(m_data(row)  < m_range_max && "One of the purposes of this class it to ensure this");
  #endif

  //Convert to string, otherwise the number digits behind the comma
  //will be set to 2, e.g. 0.01
  const std::string s = boost::lexical_cast<std::string>(m_data(row));
  return QString(s.c_str());
}

Qt::ItemFlags ribi::QtUblasVectorIntModel::flags(const QModelIndex &) const noexcept
{
  return
    Qt::ItemIsSelectable
  | Qt::ItemIsEditable
  | Qt::ItemIsDragEnabled
  | Qt::ItemIsDropEnabled
  | Qt::ItemIsEnabled;
}

std::string ribi::QtUblasVectorIntModel::GetVersion() noexcept
{
  return "1.2";
}

std::vector<std::string> ribi::QtUblasVectorIntModel::GetVersionHistory() noexcept
{
  return {
    "2013-06-27: version 1.0: initial version",
    "2013-06-27: version 1.1: added setting a range",
    "2013-07-05: version 1.2: signal layoutChanged emitted correctly"
  };
}

QVariant ribi::QtUblasVectorIntModel::headerData(int section, Qt::Orientation orientation, int role) const
{
  //Removing this line will cause checkboxes to appear
  if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();

  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    return QString( m_header_horizontal_text.c_str() );
  }
  else
  {
    assert(orientation == Qt::Vertical);
    //No idea why this happens
    if (!(section < boost::numeric_cast<int>(m_header_vertical_text.size())))
    {
      return QVariant();
    }

    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    return QString( m_header_vertical_text[section].c_str() );
  }
}

#ifndef NDEBUG
bool ribi::QtUblasVectorIntModel::IsValid() const noexcept
{
  if (m_range_min >= m_range_max)
  {
    TRACE("m_range_min bigger or equal than m_range_max");
    return false;
  }
  const std::size_t sz = m_data.size();
  for (std::size_t i=0; i!=sz; ++i)
  {
    const int x = m_data[i];
    if (x < m_range_min)
    {
      TRACE("x below minimum value");
      return false;
    }
    if (x >= m_range_max)
    {
      TRACE("x above maximum value");
      return false;
    }
  }
  return true;
}
#endif

bool ribi::QtUblasVectorIntModel::insertRows(int row, int count, const QModelIndex &parent)
{
  assert(this->IsValid());

  //Must be called before the real operation
  this->beginInsertRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() + count;

  boost::numeric::ublas::vector<int> new_data(new_size,m_range_default);
  std::copy(m_data.begin(),m_data.end(),new_data.begin());
  m_data = new_data;

  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endInsertRows();

  assert(this->IsValid());

  //It worked!
  return true;
}

bool ribi::QtUblasVectorIntModel::removeRows(int row, int count, const QModelIndex &parent)
{
  assert(this->IsValid());

  //Must be called before the real operation
  this->beginRemoveRows(parent,row,row+count-1);

  //The real operation: resize the m_data
  assert(m_data.size() == m_header_vertical_text.size());

  const int new_size = m_data.size() - count;
  m_data.resize(new_size);
  m_header_vertical_text.resize(new_size);

  assert(m_data.size() == m_header_vertical_text.size());

  //Must be called in the end
  this->endRemoveRows();

  assert(this->IsValid());

  //It worked!
  return true;
}

int ribi::QtUblasVectorIntModel::rowCount(const QModelIndex &) const noexcept
{
  assert(m_data.size() == m_header_vertical_text.size());
  return boost::numeric_cast<int>(m_data.size());
}


bool ribi::QtUblasVectorIntModel::setData(const QModelIndex &index, const QVariant &value, int /* role */)
{
  assert(this->IsValid());

  const int row = index.row();

  #ifndef NDEBUG
  const int col = index.column();
  assert(row < boost::numeric_cast<int>(m_data.size()));
  assert(col == 0);
  #endif


  bool can_convert = false;
  int new_value = value.toInt(&can_convert);
  if (!can_convert)
  {
    //Might be a double, round it to the closest integer
    new_value = boost::numeric_cast<int>(value.toFloat(&can_convert) + 0.5);
  }
  assert(m_range_min < m_range_max);
  if (can_convert && new_value >= m_range_min && new_value < m_range_max)
  {
    m_data(row) = new_value;

    assert(this->IsValid());

    ///This line below is needed to let multiple views synchronize
    emit dataChanged(index,index);

    assert(this->IsValid());

    //Editing succeeded!
    return true;
  }
  else
  {
    assert(this->IsValid());

    return false;
  }
}

bool ribi::QtUblasVectorIntModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int /*role*/)
{
  assert(this->IsValid());

  if (orientation == Qt::Horizontal)
  {
    assert(section == 0);
    assert(m_header_horizontal_text != value.toString().toStdString());
    m_header_horizontal_text = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);

    assert(this->IsValid());
    //Editing succeeded!
    return true;
  }
  else
  {
    assert(orientation == Qt::Vertical);
    assert(section < boost::numeric_cast<int>(m_header_vertical_text.size()));
    assert(m_header_vertical_text[section] != value.toString().toStdString());
    m_header_vertical_text[section] = value.toString().toStdString();
    ///This line below is needed to let multiple views synchronize
    emit headerDataChanged(orientation,section,section);

    assert(this->IsValid());
    //Editing succeeded!
    return true;
  }
}

void ribi::QtUblasVectorIntModel::SetHeaderData(
  const std::string& horizontal_header_text, const std::vector<std::string>& vertical_header_text)
{
  assert(this->IsValid());

  if (m_header_horizontal_text != horizontal_header_text)
  {
    emit layoutAboutToBeChanged();
    assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
    m_header_horizontal_text = horizontal_header_text;

    assert(this->IsValid());

    emit layoutChanged();
    emit headerDataChanged(Qt::Horizontal,0,1);
  }

  if (m_header_vertical_text != vertical_header_text)
  {
    emit layoutAboutToBeChanged();
    const int new_size = boost::numeric_cast<int>(vertical_header_text.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }
    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_header_vertical_text = vertical_header_text;

    assert(this->rowCount() == boost::numeric_cast<int>(vertical_header_text.size())
      && "So emit layoutChange can work on the newest layout");

    emit layoutChanged();

    emit headerDataChanged(Qt::Vertical,0,new_size);
  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
  assert(this->IsValid());
}

void ribi::QtUblasVectorIntModel::SetRange(const int from, const int to, const int default_value)
{
  assert(this->IsValid());
  assert(from < to);
  assert(default_value < to);
  assert(default_value >= from);
  this->m_range_min = from;
  this->m_range_max = to;
  this->m_range_default = default_value;
  #ifndef NDEBUG
  {
    const std::size_t sz = m_data.size();
    for (std::size_t i=0; i!=sz; ++i)
    {
      const int x = m_data[i];
      assert(x >= m_range_min && x < m_range_max
        && "Before changing the range, the data in QtUblasVectorIntModel must be"
           " put in both the old and new range"); //Better a clear clumsy interface over a vague one
    }
  }
  assert(this->IsValid());
  #endif
}

void ribi::QtUblasVectorIntModel::SetRawData(const boost::numeric::ublas::vector<int>& data)
{
  //Check if all data is in range
  #ifndef NDEBUG
  assert(this->IsValid());
  {
    const std::size_t sz = data.size();
    for (std::size_t i=0; i!=sz; ++i)
    {
      const int x = data[i];
      assert(x >= m_range_min && x < m_range_max
        && "Supplied data to QtUblasVectorIntModel must be in range");
    }
  }
  #endif

  if (!Matrix::VectorsIntAreEqual(m_data,data))
  {
    const int new_size = boost::numeric_cast<int>(data.size());
    const int cur_size = this->rowCount();
    if (cur_size < new_size)
    {
      this->insertRows(cur_size,new_size - cur_size,QModelIndex());
    }
    else if (cur_size > new_size)
    {
      this->removeRows(cur_size,cur_size - new_size,QModelIndex());
    }
    //Set the data before emitting signals, as the response to that signal
    //will be dependent on that data
    m_data = data;

    assert(this->rowCount() == boost::numeric_cast<int>(data.size())
      && "So emit layoutChange can work on the newest layout");

    emit layoutChanged();

    const QModelIndex top_left = this->index(0,0);
    const QModelIndex bottom_right = this->index(m_data.size() - 1, 1);
    emit dataChanged(top_left,bottom_right);


    assert(this->IsValid());
  }

  assert(this->rowCount() == boost::numeric_cast<int>(this->m_data.size()));
  assert(this->rowCount() == boost::numeric_cast<int>(m_header_vertical_text.size()));
  assert(this->columnCount() == (this->rowCount() == 0 ? 0 : 1));
  assert(this->IsValid());
}

#ifndef NDEBUG
void ribi::QtUblasVectorIntModel::Test() noexcept
{
  {
    static bool is_tested{false};
    if (is_tested) return;
    is_tested = true;
  }
  {
    QtUblasVectorIntModel d;
    d.SetRawData( Matrix::CreateVector( { 1,2,3 } ) );
    d.SetRange(1,4,1);
    assert(d.data(d.index(0,0)).toInt() == 1);
    d.setData(d.index(0,0),"0"); //Should not change the data
    assert(d.data(d.index(0,0)).toInt() == 1);
  }
}
#endif

 

 

 

 

 

Go back to Richel Bilderbeek's C++ page.

Go back to Richel Bilderbeek's homepage.

 

Valid XHTML 1.0 Strict

This page has been created by the tool CodeToHtml