I'm reading a ~2 MB json file to a QAbstractItemModel visualised by a QTreeView. Each json object converts to a row in the model. The program consumes +700 MB in memory. This ratio of storage space and memory usage is not very satisfying.
I assume, the QAbstractItemModel is responsible:
Using a very small json file, memory consumption drops to ~50 MB.
Disabling the views setModel function does not change the memory consumption significantly.
Is this the behavior to be expected? I assume not.
Is there a way to optimise the memory usage? I assume this should be handled in the models data item realisation. I attached my RowItem realisation in the following:
.h file:
class RowItem
{
public:
explicit RowItem();
virtual ~RowItem();
void setParent( RowItem * p_pParent );
void appendChild( RowItem * p_pChild );
void insertChild( int p_nRow, RowItem * p_pChild );
bool removeChild(int p_nRow);
bool replaceChild(RowItem * p_pOldChild , RowItem *p_pNewChild);
RowItem * child( int p_nRow ) const;
int rowCount() const;
int columnCount() const;
int column( const QVariant & p_grData, int p_nRole = Qt::DisplayRole ) const;
QVariant data(const int & p_nColumn, int p_nRole = Qt::DisplayRole ) const;
int row() const;
RowItem * parentItem();
bool setData( const int & p_nColumn, const QVariant & p_grData, int p_nRole = Qt::DisplayRole );
RowItem & operator=( RowItem & pSrc );
protected:
QList< RowItem * > m_pChildItems;
QList< QMap< quint8, QVariant > > m_grColList; /// #attention List index = column; value map: quint8 = p_nRole ( e.g. Qt::DisplayRole, limited to max 256), QVarinat = data
RowItem * m_pParentItem;
};
.cpp file
RowItem::RowItem() : m_pParentItem( nullptr )
{
}
RowItem::~RowItem()
{
qDeleteAll(m_pChildItems );
}
void RowItem::setParent(RowItem *p_pParent)
{
m_pParentItem = p_pParent;
}
void RowItem::appendChild(RowItem *p_pChild)
{
m_pChildItems.append(p_pChild);
p_pChild->setParent( this );
}
void RowItem::insertChild(int p_nRow, RowItem *p_pChild)
{
m_pChildItems.insert( p_nRow, p_pChild );
p_pChild->setParent( this );
}
bool RowItem::removeChild(int p_nRow )
{
if ( m_pChildItems.size() <= p_nRow ) {
return false;
}
RowItem * pChild = m_pChildItems.at( p_nRow );
delete pChild;
pChild = nullptr;
m_pChildItems.removeAt( p_nRow );
return true;
}
bool RowItem::replaceChild(RowItem * p_pOldChild, RowItem * p_pNewChild )
{
if ( ( p_pOldChild == nullptr ) || ( p_pNewChild == nullptr ) ) {
return false;
}
int nIdx = m_pChildItems.indexOf( p_pOldChild );
if ( nIdx == -1 ) {
return false;
}
m_pChildItems.replace( nIdx, p_pNewChild );
p_pNewChild->setParent( this );
return true;
}
RowItem *RowItem::child(int p_nRow) const
{
if ( ( p_nRow >= m_pChildItems.size() ) || ( p_nRow < 0 ) )
{
QLOG_ERROR() << "Requested child item not existent. m_pChildItems.size() = " << m_pChildItems.size() << "; requested item:" << p_nRow << Q_FUNC_INFO;
return nullptr;
}
return m_pChildItems.at(p_nRow);
}
int RowItem::rowCount() const
{
return m_pChildItems.count();
}
int RowItem::columnCount() const
{
return m_grColList.count();
}
int RowItem::column(const QVariant &p_grData, int /*p_nRole*/) const
{
for( int i = 0; i < m_grColList.size(); ++i ) {
const QMap< quint8, QVariant > & grRoleMap = m_grColList.at( i );
if( grRoleMap.values().contains( p_grData ) == true ) {
return i;
}
}
return -1;
}
QVariant RowItem::data(const int &p_nColumn, int p_nRole) const
{
if ( ( p_nColumn < 0 ) || ( p_nRole < 0 ) ) {
QLOG_ERROR() << "Requested invalid column or role data:" << p_nColumn << p_nRole << Q_FUNC_INFO;
return QVariant();
}
if ( m_grColList.size() <= p_nColumn ) {
QLOG_ERROR() << "Requested column does not exists. Column size is " << m_grColList.size() << "; requested was col" << p_nColumn << Q_FUNC_INFO;
return QVariant();
}
const QMap< quint8, QVariant > & grRoleMap = m_grColList.at( p_nColumn );
QVariant grValue = grRoleMap.value( p_nRole, QVariant() );
if ( ( p_nRole == Qt::EditRole ) && ( ! grValue.isValid() ) ) {
grValue = grRoleMap.value( Qt::DisplayRole, QVariant() );
}
return grValue;
}
int RowItem::row() const
{
int nRow = 0;
if ( m_pParentItem != nullptr ) {
nRow = m_pParentItem->m_pChildItems.indexOf( const_cast< RowItem* >( this ) );
}
return nRow;
}
RowItem *RowItem::parentItem()
{
return m_pParentItem;
}
bool RowItem::setData( const int & p_nColumn, const QVariant &p_grData , int p_nRole)
{
QMap< quint8 , QVariant > grRoleMap;
int nCol = p_nColumn;
if ( p_nColumn == -1 ) {
m_grColList.append( QMap< quint8 , QVariant >() );
nCol = m_grColList.size() - 1;
}
else {
// expand coloumns if required
while( m_grColList.size() <= p_nColumn ) {
m_grColList.append( QMap< quint8 , QVariant >() );
}
grRoleMap = m_grColList.at( p_nColumn );
}
grRoleMap.insert( p_nRole, p_grData );
m_grColList.replace( nCol, grRoleMap );
return true;
}
RowItem &RowItem::operator=(RowItem &p_grSrc)
{
m_grColList = p_grSrc.m_grColList;
return * this;
}
Related
Here is my source.cpp:
#include "BST.h"
using namespace std;
int main(){
BST<int> test1;
BST<int> test2;
test1.insert(10);
test1.insert(15);
test1.insert(12);
test1.insert(14);
test1.insert(19);
test1.test();
cout << test1.contain(1) << endl;
cout << test1.isEmpty() << endl;
//cout << test2.isEmpty() << endl;
cin.get();
return 0;
}
And here is my header file:
//Haris
#include <iostream>
#include "Vector.h"
template <typename Comparable>
class BST
{
public:
int size = 0;
int currentRootindex = 0;
BST(){
for (int i = 0; i < 1000; i++){
data[i] = NULL;
}
}
~BST(){
}
void insert(Comparable obj){
if (data[0] == NULL){
data[0] = obj;
size++;
}
else{
for (int index = 0; index < size; index++){
if (data[currentRootindex] < obj && data[(2 * currentRootindex) + 2] == NULL){
data[(2 * currentRootindex) + 2] = obj;
size++;
break;
}
else if (data[currentRootindex] >= obj && data[(2 * currentRootindex) + 1] == NULL){
data[(2 * currentRootindex) + 1] = obj;
size++;
break;
}
else if (data[currentRootindex] < obj){
currentRootindex = ((2 * currentRootindex) + 2);
}
else{
currentRootindex = ((2 * currentRootindex) + 1);
}
}
}
currentRootindex = 0;
}
bool isEmpty(){
if (data[0] == NULL){
return true;
}
else
return false;
}
bool contain(Comparable obj){
for (int index = 0; index < size; index++){
if (data[currentRootindex] < obj){
currentRootindex = ((2 * currentRootindex) + 2);
}
else if (data[currentRootindex] > obj){
currentRootindex = ((2 * currentRootindex) + 1);
}
else
return true;
}
return false;
}
void test(){
cout << data[5] << endl;
}
private:
Vector<Comparable> data;
};
if I comment out the BST<int>test2 from source.cpp, the code works as intended.
But when i instantiate BST<int>test2 after BST<int>test1, I get Application Error: The memory could not be read. I have not declared any static variables, so none of the variables is being shared across different objects.
Also, the Vector header file, I am using is
#ifndef VECTOR_H
#define VECTOR_H
#include <algorithm>
#include <iostream>
template <typename Object>
class Vector
{
public:
explicit Vector( int initSize = 0 )
: theSize{ initSize }, theCapacity{ initSize + SPARE_CAPACITY }
{ objects = new Object[ theCapacity ]; }
Vector( const Vector & rhs )
: theSize{ rhs.theSize }, theCapacity{ rhs.theCapacity }, objects{ nullptr }
{
objects = new Object[ theCapacity ];
for( int k = 0; k < theSize; ++k )
objects[ k ] = rhs.objects[ k ];
}
Vector & operator= ( const Vector & rhs )
{
Vector copy = rhs;
std::swap( *this, copy );
return *this;
}
~Vector( )
{ delete [ ] objects; }
Vector( Vector && rhs )
: theSize{ rhs.theSize }, theCapacity{ rhs.theCapacity }, objects{ rhs.objects }
{
rhs.objects = nullptr;
rhs.theSize = 0;
rhs.theCapacity = 0;
}
Vector & operator= ( Vector && rhs )
{
std::swap( theSize, rhs.theSize );
std::swap( theCapacity, rhs.theCapacity );
std::swap( objects, rhs.objects );
return *this;
}
bool empty( ) const
{ return size( ) == 0; }
int size( ) const
{ return theSize; }
int capacity( ) const
{ return theCapacity; }
Object & operator[]( int index )
{
return objects[ index ];
}
const Object & operator[]( int index ) const
{
return objects[ index ];
}
void resize( int newSize )
{
if( newSize > theCapacity )
reserve( newSize * 2 );
theSize = newSize;
}
void reserve( int newCapacity )
{
if( newCapacity < theSize )
return;
Object *newArray = new Object[ newCapacity ];
for( int k = 0; k < theSize; ++k )
newArray[ k ] = std::move( objects[ k ] );
theCapacity = newCapacity;
std::swap( objects, newArray );
delete [ ] newArray;
}
// Stacky stuff
void push_back( const Object & x )
{
if( theSize == theCapacity )
reserve( 2 * theCapacity + 1 );
objects[ theSize++ ] = x;
}
// Stacky stuff
void push_back( Object && x )
{
if( theSize == theCapacity )
reserve( 2 * theCapacity + 1 );
objects[ theSize++ ] = std::move( x );
}
void pop_back( )
{
--theSize;
}
const Object & back ( ) const
{
return objects[ theSize - 1 ];
}
// Iterator stuff: not bounds checked
typedef Object * iterator;
typedef const Object * const_iterator;
iterator begin( )
{ return &objects[ 0 ]; }
const_iterator begin( ) const
{ return &objects[ 0 ]; }
iterator end( )
{ return &objects[ size( ) ]; }
const_iterator end( ) const
{ return &objects[ size( ) ]; }
static const int SPARE_CAPACITY = 2;
private:
int theSize;
int theCapacity;
Object * objects;
};
#endif
You're accessing out of bounds indices of the data vector. The default vector constructor initialises with size 0 (although it allocates 2 extra elements) but then you immediately try to access up to index 999:
BST(){
for (int i = 0; i < 1000; i++){
data[i] = NULL;
}
}
The second object isn't really related to the problem. Even with only one object, the code is still accessing memory it shouldn't.
Changing the data vector to initialise with size 1000 fixes the out of bounds errors for this particular code, but I suspect the BST class will cause more of these errors in future because I can't see any checks against the data vector's size.
info:
We are trying to create a property whose sub properties may be added/removed depending on the value of another sub property.
An example of this could be an plant object. This object has one property which will be a drop down consisting of types of plants, lets say (daisy, daffodil, and venus fly trap). If venus is selected, I would like to have a property show up below called avgFlyIntake but if daffodil is selected this property should disappear.
example:
Plant
type venus
avgFlyI 2.0
avgHight 3.0
Or
Plant
type daffodil
avgHight 8.0
//Fly Property gone or grayed out.
Edit: Essentially plant is a property of another model that I guess could be called livingThing. The property's show up in a hierarchical fashion in a 2 column table. I'd like the plant properties to be dynamically hidden if it is possible.
c++ src:
PlantProperty::PlantProperty(const QString& name /*= QString()*/, QObject* propertyObject /*= 0*/, QObject* parent /*= 0*/)
: Property(name, propertyObject, parent)
{
m_type = new Property("id", this, this);
m_avgFlyIntake = new Property("avgFlyIntake", this, this);
m_avgHeight = new Property("avgHeight", this, this);
//setEditorHints("...");
}
enum PlantProperty::getType() const{
return value().value<Plant>().getType();
}
void PlantProperty::setType(enum enabled){
//if enum changed show and hide relevant properties associated with selection
Property::setValue(QVariant::fromValue(Plant(...)));
}
c++ h:
class PlantProperty : public Property{
Q_OBJECT
/**/
Q_PROPERTY(int type READ getType WRITE setType DESIGNABLE true USER true)
Q_PROPERTY(float avgFlyIntake READ getavgFlyIntake WRITE setAvgFlyIntake DESIGNABLE true USER ture)
Q_PROPERTY(float avgHeight READ getavgHeight WRITE setavgHeihgt DESIGNABLE true USER true)
public:
PlantProperty (const QString&name = QString(), QObject* propertyObject = 0, QObject* parent = 0);
QVariant data(const QModelIndex &index, int role) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
QVariant value(int role = Qt::UserRole) const;
virtual void setValue(const QVariant& value);
void setEditorHints(const QString& hints);
int getType() const;
void setType(int id);
private:
QString parseHints(const QString& hints, const QChar component);
Property* type;
Property* avgFlyIntake;
Property* avgHeight;
};
Q_DECLARE_METATYPE(Plant)
#endif //PLANTPROPERTY_H
q property model:
// *************************************************************************************************
//
// QPropertyEditor v 0.3
//
// --------------------------------------
// Copyright (C) 2007 Volker Wiendl
// Acknowledgements to Roman alias banal from qt-apps.org for the Enum enhancement
//
//
// The QPropertyEditor Library 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 version 3 of the License
//
// The Horde3D Scene Editor 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/>.
//
// *************************************************************************************************
#include "QPropertyModel.h"
#include "Property.h"
#include "EnumProperty.h"
#include <QtGui/QApplication>
#include <QtCore/QMetaProperty>
#include <QtGui/QItemEditorFactory>
struct PropertyPair
{
PropertyPair(const QMetaObject* obj, QMetaProperty property) : Property(property), Object(obj) {}
QMetaProperty Property;
const QMetaObject* Object;
bool operator==(const PropertyPair& other) const {return QString(other.Property.name()) == QString(Property.name());}
};
QPropertyModel::QPropertyModel(QObject* parent /*= 0*/) : QAbstractItemModel(parent)
{
m_rootItem = new Property("Root",0, this);
}
QPropertyModel::~QPropertyModel()
{
}
QModelIndex QPropertyModel::index ( int row, int column, const QModelIndex & parent /*= QModelIndex()*/ ) const
{
Property *parentItem = m_rootItem;
if (parent.isValid())
parentItem = static_cast<Property*>(parent.internalPointer());
if (row >= parentItem->children().size() || row < 0)
return QModelIndex();
return createIndex(row, column, parentItem->children().at(row));
}
QModelIndex QPropertyModel::parent ( const QModelIndex & index ) const
{
if (!index.isValid())
return QModelIndex();
Property *childItem = static_cast<Property*>(index.internalPointer());
Property *parentItem = qobject_cast<Property*>(childItem->parent());
if (!parentItem || parentItem == m_rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int QPropertyModel::rowCount ( const QModelIndex & parent /*= QModelIndex()*/ ) const
{
Property *parentItem = m_rootItem;
if (parent.isValid())
parentItem = static_cast<Property*>(parent.internalPointer());
return parentItem->children().size();
}
int QPropertyModel::columnCount ( const QModelIndex & /*parent = QModelIndex()*/ ) const
{
return 2;
}
QVariant QPropertyModel::data ( const QModelIndex & index, int role /*= Qt::DisplayRole*/ ) const
{
if (!index.isValid())
return QVariant();
Property *item = static_cast<Property*>(index.internalPointer());
switch(role)
{
case Qt::ToolTipRole:
case Qt::DecorationRole:
case Qt::DisplayRole:
case Qt::EditRole:
if (index.column() == 0)
return item->objectName().replace('_', ' ');
if (index.column() == 1)
return item->value(role);
case Qt::BackgroundRole:
if (item->isRoot()) return QApplication::palette("QTreeView").brush(QPalette::Normal, QPalette::Button).color();
break;
};
return QVariant();
}
// edit methods
bool QPropertyModel::setData ( const QModelIndex & index, const QVariant & value, int role /*= Qt::EditRole*/ )
{
if (index.isValid() && role == Qt::EditRole)
{
Property *item = static_cast<Property*>(index.internalPointer());
item->setValue(value);
emit dataChanged(index, index);
return true;
}
return false;
}
Qt::ItemFlags QPropertyModel::flags ( const QModelIndex & index ) const
{
if (!index.isValid())
return Qt::ItemIsEnabled;
Property *item = static_cast<Property*>(index.internalPointer());
// only allow change of value attribute
if (item->isRoot())
return Qt::ItemIsEnabled;
else if (item->isReadOnly())
return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable;
else
return Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}
QVariant QPropertyModel::headerData ( int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/ ) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
{
switch (section)
{
case 0:
return tr("Name");
case 1:
return tr("Value");
}
}
return QVariant();
}
QModelIndex QPropertyModel::buddy ( const QModelIndex & index ) const
{
if (index.isValid() && index.column() == 0)
return createIndex(index.row(), 1, index.internalPointer());
return index;
}
void QPropertyModel::addItem(QObject *propertyObject)
{
// first create property <-> class hierarchy
QList<PropertyPair> propertyMap;
QList<const QMetaObject*> classList;
const QMetaObject* metaObject = propertyObject->metaObject();
do
{
int count = metaObject->propertyCount();
for (int i=0; i<count; ++i)
{
QMetaProperty property = metaObject->property(i);
if( property.isUser() ) // Hide Qt specific properties
{
PropertyPair pair(metaObject, property);
int index = propertyMap.indexOf(pair);
if (index != -1)
propertyMap[index] = pair;
else
propertyMap.push_back(pair);
}
}
classList.push_front(metaObject);
}
while ((metaObject = metaObject->superClass())!=0);
QList<const QMetaObject*> finalClassList;
// remove empty classes from hierarchy list
foreach(const QMetaObject* obj, classList)
{
bool keep = false;
foreach(PropertyPair pair, propertyMap)
{
if (pair.Object == obj)
{
keep = true;
break;
}
}
if (keep)
finalClassList.push_back(obj);
}
// finally insert properties for classes containing them
int i=rowCount();
Property* propertyItem = 0;
beginInsertRows( QModelIndex(), i, i + finalClassList.count() );
foreach(const QMetaObject* metaObject, finalClassList)
{
// Set default name of the hierarchy property to the class name
QString name = metaObject->className();
// Check if there is a special name for the class
int index = metaObject->indexOfClassInfo(qPrintable(name));
if (index != -1)
name = metaObject->classInfo(index).value();
// Create Property Item for class node
propertyItem = new Property(name, 0, m_rootItem);
foreach(PropertyPair pair, propertyMap)
{
// Check if the property is associated with the current class from the finalClassList
if (pair.Object == metaObject)
{
QMetaProperty property(pair.Property);
Property* p = 0;
if (property.type() == QVariant::UserType && !m_userCallbacks.isEmpty())
{
QList<QPropertyEditorWidget::UserTypeCB>::iterator iter = m_userCallbacks.begin();
while( p == 0 && iter != m_userCallbacks.end() )
{
p = (*iter)(property.name(), propertyObject, propertyItem);
++iter;
}
}
if( p == 0){
if(property.isEnumType()){
p = new EnumProperty(property.name(), propertyObject, propertyItem);
} else {
p = new Property(property.name(), propertyObject, propertyItem);
}
}
int index = metaObject->indexOfClassInfo(property.name());
if (index != -1)
p->setEditorHints(metaObject->classInfo(index).value());
}
}
}
endInsertRows();
if( propertyItem ) addDynamicProperties( propertyItem, propertyObject );
}
void QPropertyModel::updateItem ( QObject* propertyObject, const QModelIndex& parent /*= QModelIndex() */ )
{
Property *parentItem = m_rootItem;
if (parent.isValid())
parentItem = static_cast<Property*>(parent.internalPointer());
if (parentItem->propertyObject() != propertyObject)
parentItem = parentItem->findPropertyObject(propertyObject);
if (parentItem) // Indicate view that the data for the indices have changed
{
QModelIndex itemIndex = createIndex(parentItem->row(), 0, static_cast<Property*>(parentItem));
dataChanged(itemIndex, createIndex(parentItem->row(), 1, static_cast<Property*>(parentItem)));
QList<QByteArray> dynamicProperties = propertyObject->dynamicPropertyNames();
QList<QObject*> childs = parentItem->parent()->children();
int removed = 0;
for(int i = 0; i < childs.count(); ++i )
{
QObject* obj = childs[i];
if( !obj->property("__Dynamic").toBool() || dynamicProperties.contains( obj->objectName().toLocal8Bit() ) )
continue;
beginRemoveRows(itemIndex.parent(), i - removed, i - removed);
++removed;
delete obj;
endRemoveRows();
}
addDynamicProperties(static_cast<Property*>(parentItem->parent()), propertyObject);
}
}
void QPropertyModel::addDynamicProperties( Property* parent, QObject* propertyObject )
{
// Get dynamic property names
QList<QByteArray> dynamicProperties = propertyObject->dynamicPropertyNames();
QList<QObject*> childs = parent->children();
// Remove already existing properties from list
for(int i = 0; i < childs.count(); ++i )
{
if( !childs[i]->property("__Dynamic").toBool() ) continue;
int index = dynamicProperties.indexOf( childs[i]->objectName().toLocal8Bit() );
if( index != -1)
{
dynamicProperties.removeAt(index);
continue;
}
}
// Remove invalid properites and those we don't want to add
for(int i = 0; i < dynamicProperties.size(); ++i )
{
QString dynProp = dynamicProperties[i];
// Skip properties starting with _ (because there may be dynamic properties from Qt with _q_ and we may
// have user defined hidden properties starting with _ too
if( dynProp.startsWith("_") || !propertyObject->property( qPrintable(dynProp) ).isValid() )
{
dynamicProperties.removeAt(i);
--i;
}
}
if( dynamicProperties.empty() ) return;
QModelIndex parentIndex = createIndex(parent->row(), 0, static_cast<Property*>(parent));
int rows = rowCount(parentIndex);
beginInsertRows(parentIndex, rows, rows + dynamicProperties.count() - 1 );
// Add properties left in the list
foreach(QByteArray dynProp, dynamicProperties )
{
QVariant v = propertyObject->property(dynProp);
Property* p = 0;
if( v.type() == QVariant::UserType && !m_userCallbacks.isEmpty() )
{
QList<QPropertyEditorWidget::UserTypeCB>::iterator iter = m_userCallbacks.begin();
while( p == 0 && iter != m_userCallbacks.end() )
{
p = (*iter)(dynProp, propertyObject, parent);
++iter;
}
}
if( p == 0 ) p = new Property(dynProp, propertyObject, parent);
p->setProperty("__Dynamic", true);
}
endInsertRows();
}
void QPropertyModel::clear()
{
beginRemoveRows(QModelIndex(), 0, rowCount());
delete m_rootItem;
m_rootItem = new Property("Root",0, this);
endRemoveRows();
}
void QPropertyModel::registerCustomPropertyCB(QPropertyEditorWidget::UserTypeCB callback)
{
if ( !m_userCallbacks.contains(callback) )
m_userCallbacks.push_back(callback);
}
void QPropertyModel::unregisterCustomPropertyCB(QPropertyEditorWidget::UserTypeCB callback)
{
int index = m_userCallbacks.indexOf(callback);
if( index != -1 )
m_userCallbacks.removeAt(index);
}
QObject has a dynamic property system, so you do not need to manually declare any properties at all. It'd be easy to map such objects to an item view. But it seems like the use of a QObject is overkill.
It seems like you'd be completely covered by using a QStandardItemModel and its items, arranged in a tree. An item could be either a plant, or a plant's property.
I must adding a row in a QTableWidget. Previously I add all rows with a size of a list, like this:
MyProgram::MyProgram( QWidget* parent )
: QDialog( parent )
, ui( new Ui::MyProgram )
{
ui->setupUi( this );
QStringList allFiles = QDir( "~/" ).entryList( QStringList << "*.txt" );
ui->myQTableWidget->setRowCount( allFiles.size() );
for( int cont = 0; cont < allFiles.size(); ++cont )
{
//insert values in my QTableWidget
}
}
But now I can't know how many files I will show in QTableWidget, because I added a validation before. It like this:
MyProgram::MyProgram( QWidget* parent )
: QDialog( parent )
, ui( new Ui::MyProgram )
{
ui->setupUi( this );
QStringList allFiles = QDir( "~/" ).entryList( QStringList << "*.txt" );
for( int cont = 0; cont < allFiles.size(); ++cont )
{
bool ok = true;
try {
//try something
} catch( //exception )
{
ok = false;
}
if (ok) {
//insert values in my QTableWidget
}
}
}
How I can add a row in a QTableWidget without knowing how many items this will have?
This works:
MyProgram::MyProgram( QWidget* parent )
: QDialog( parent )
, ui( new Ui::MyProgram )
{
ui->setupUi( this );
QStringList allFiles = QDir( "~/" ).entryList( QStringList << "*.txt" );
for( int cont = 0; cont < allFiles.size(); ++cont )
{
bool ok = true;
try {
//try something
} catch( //exception )
{
ok = false;
}
if (ok) {
int row = ui->myQTableWidget->rowCount();
ui->myQTableWidget->insertRow( row );
// setItems
}
}
}
I played around with a lot of different forward declaration combinations, this seemed like the best one. It was the only one that compiled, until I uncommented the line
e->process( this );
And I get an error from xcode that says:
non-const lvalue reference to type 'Event::ModemSimV2' cannot bind to a temporary type 'ModemSimV2'
I don't really understand what it means, any help would be appreciated.
Thanks,
Source:
#include "ModemSimV2.h"
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Event +++++++++++++++++++++++++++++
Event::Event(){
}
Event::Event( const Event &e ) {
*this = e;
}
Event::~Event( ) {
}
/*
bool Event::operator > ( const Event & rhs ) const {
return time > rhs.time;
}
bool Event::operator < ( const Event & rhs ) const {
return time < rhs.time;
}
bool Event::operator <= ( const Event & rhs ) const {
return time < rhs.time;
}
bool Event::operator != ( const Event & rhs ) const {
return time != rhs.time;
}
*/
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Dialin +++++++++++++++++++++++++++++
Dialin::Dialin (int name, int tm )
: time( tm ), who( name ) {
return;
}
Dialin::Dialin ( const Dialin &d ) {
*this = d;
}
Dialin::~Dialin( ) {
}
void Dialin::process( ModemSimV2 &m ) {
}
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ EventHeap ++++++++++++++++++++++++++++
EventHeap::EventHeap( ) {
size = 0;
}
EventHeap::EventHeap( int numVals ) {
size = 0;
}
//insert
void EventHeap::push( const Event e ) {
*array[size] = e;
reIndex( size );
size++;
}
//removes the min val
Event* EventHeap::pop( ) {
Event *e = array[0];
array[0] = array[size - 1];
size--;
if( !empty( ) )
buildHeap(0);
return e;
}
//re do
void EventHeap::buildHeap( int nodeIndex ) {
int leftChildIndex, rightChildIndex, minIndex;
Event *tmp;
leftChildIndex = getLeft(nodeIndex);
rightChildIndex = getRight(nodeIndex);
if (rightChildIndex >= size) {
if (leftChildIndex >= size)
return;
else
minIndex = leftChildIndex;
} else {
if (array[leftChildIndex] <= array[rightChildIndex])
minIndex = leftChildIndex;
else
minIndex = rightChildIndex;
}
if (array[nodeIndex] > array[minIndex]) {
tmp = array[minIndex];
array[minIndex] = array[nodeIndex];
array[nodeIndex] = tmp;
buildHeap(minIndex);
}
}
//re index
void EventHeap::reIndex( int hole ) {
while( array[hole] != NULL && array[hole] < array[getParent( hole )] ) {
int pIndex = getParent( hole );
Event *temp( array[hole] );
array[hole] = array[pIndex];
array[pIndex] = temp;
hole = pIndex;
}
}
//is Empty
bool EventHeap::empty() const {
return ( size == 0 );
}
int EventHeap::getLeft( int index ) const {
return ( index * 2 ) + 1;
}
int EventHeap::getRight( int index ) const {
return ( index * 2 ) + 2;
}
int EventHeap::getParent( int index ) const {
return ( index - 1 ) / 2;
}
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ ModemSimV2 +++++++++++++++++++++++++++
// Constructor for ModemSim.
ModemSimV2::ModemSimV2( int modems, double avgLen, int callIntrvl, EventHeap e )
: freeModems( modems ), avgCallLen( avgLen ),
freqOfCalls( callIntrvl ), r( (int) time( 0 ) )
{
eventSet = &e;
nextCall( freqOfCalls ); // Schedule first call
}
// Place a new DIAL_IN event into the event queue.
// Then advance the time when next DIAL_IN event will occur.
// In practice, we would use a random number to set the time.
void ModemSimV2::nextCall( int delta ){
static int nextCallTime = 0;
static int userNum = 0;
Event *e;
Dialin d = Dialin( userNum++, nextCallTime );
*e = d;
eventSet->push( *e );
nextCallTime += delta;
}
// Run the simulation until stopping time occurs.
void ModemSimV2::runSim( int stoppingTime ){
Event *e;
while( !eventSet->empty( ) ){
e = eventSet->pop();
if ( e->getTime() > stoppingTime )
break;
e->process( this );
nextCall( freqOfCalls );
}
}
Header:
#ifndef MODEM_SIM_V2_H
#define MODEM_SIM_V2_H
#include <queue>
#include <vector>
#include <functional> // for greater()
#include <climits> // for INT_MAX
#include <iostream>
#include "random.h"
using namespace std;
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Event +++++++++++++++++++++++++++++
class Event{
protected:
int who; // the number of the user
int time; // when the event will occur
int what; // DIAL_IN or HANGUP
class ModemSimV2;
public:
Event( );
Event( const Event &e );
virtual ~Event( );
bool operator > ( const Event & rhs ) const;
bool operator < ( const Event & rhs ) const;
bool operator <= ( const Event & rhs ) const;
bool operator != ( const Event & rhs ) const;
int getTime( ) { return time; };
virtual void process( ModemSimV2 &m ) = 0;
};
class Dialin : public Event{
public:
Dialin( int name = 0, int tm = 0 );
Dialin( const Dialin &d );
~Dialin( );
virtual void process( ModemSimV2 &m );
private:
int who;
int time;
int what;
};
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ EventHeap ++++++++++++++++++++++++++++
class EventHeap{
public:
EventHeap();
EventHeap( int numIndex );
bool empty( ) const;
const int & findMin( ) const;
void push( const Event x );
Event * pop();
private:
int size; // Number of elements in heap
vector <Event*> array; // The heap array
void buildHeap( int index );
void reIndex( int hole );
int getLeft( int index ) const;
int getRight( int index )const;
int getParent( int index )const;
};
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ ModemSimV2 +++++++++++++++++++++++++++
class ModemSimV2{
public:
ModemSimV2( int modems, double avgLen, int callIntrvl, EventHeap e );
// Add a call to eventSet at the current time,
// and schedule one for delta in the future.
void nextCall( int delta );
// Run the simulation
void runSim( int stoppingTime );// = INT_MAX );
friend class Event;
private:
Random r; // A random source
EventHeap *eventSet; // Pending events
// Basic parameters of the simulation
int freeModems; // Number of modems unused
const double avgCallLen; // Length of a call
const int freqOfCalls; // Interval between calls
};
#endif
The problem is you put your forward declaration in the wrong place in the header. The process method thinks that ModemSimV2 belongs to Event, hence Event::ModemSimV2 in the error message. Move class ModemSimV2; out of the protected section to up above the class.
class ModemSimV2;
class Event
{
...
Also this is a pointer to ModemSimV2 and you need to dereference it before passing it to process.
e->process(*this);
I'm having a problem assigning pointers to an array of pointers in my program. I have it setup so that the function accepts a pointer as an argument and I tried to assign that pointer to an index of the array. It compiles fine, but it does not run, I get a segfault.
The error comes from EventHeap::push, the first line in the function.
You guys have any ideas?
I appreciate the help.
Edit: I'll post the specific problem right down below, but I'll just leave the code just in case below it :D
The segfault occurs in the push function, first line.
void ModemSimV2::nextCall( int delta ){
static int nextCallTime = 0;
static int userNum = 0;
Event *e;
e = new Dialin( userNum++, nextCallTime );
eventSet->push( e );
nextCallTime += delta;
}
//push function takes a pointer to an Event and puts into the array
void EventHeap::push( Event *e ) {
array[size] = e;
reIndex( size );
size++;
}
Source:
// ModemSimV2.cpp
//
// #author David Harrigan
// dtk24
//
// 4/10/2012
//
#include "ModemSimV2.h"
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ ModemSimV2 +++++++++++++++++++++++++++
// Constructor for ModemSim.
ModemSimV2::ModemSimV2( int modems, double avgLen, int callIntrvl )
: freeModems( modems ), avgCallLen( avgLen ),
freqOfCalls( callIntrvl ), r( (int) time( 0 ) )
{
nextCall( freqOfCalls ); // Schedule first call
}
// Place a new DIAL_IN event into the event queue.
// Then advance the time when next DIAL_IN event will occur.
// In practice, we would use a random number to set the time.
void ModemSimV2::nextCall( int delta ){
static int nextCallTime = 0;
static int userNum = 0;
Event *e;
e = new Dialin( userNum++, nextCallTime );
eventSet->push( e );
nextCallTime += delta;
}
// Run the simulation until stopping time occurs.
void ModemSimV2::runSim( int stoppingTime ){
Event *e;
int i = 0;
while( ! eventSet->empty( ) ){
e = eventSet->pop();
if ( e->getTime() > stoppingTime )
break;
if ( e->process( *this ) )
eventSet->push( e );
nextCall( freqOfCalls );
i++;
}
}
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Event +++++++++++++++++++++++++++++
//Constructor for event
Event::Event(){
}
//copy constructor
Event::Event( const Event &e ) {
*this = e;
}
//virtual destructor
Event::~Event( ) {
}
//operator overloads
bool Event::operator > ( const Event & rhs ) const {
return time > rhs.time;
}
bool Event::operator < ( const Event & rhs ) const {
return time < rhs.time;
}
bool Event::operator <= ( const Event & rhs ) const {
return time < rhs.time;
}
bool Event::operator != ( const Event & rhs ) const {
return time != rhs.time;
}
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Dialin +++++++++++++++++++++++++++++
//inhereited constructor
Dialin::Dialin (int name, int tm )
: time( tm ), who( name ) {
return;
}
//copy constructor
Dialin::Dialin ( const Dialin &d ) {
*this = d;
}
//destructor
Dialin::~Dialin( ) {
}
//bool process - unfinished
bool Dialin::process( ModemSimV2 &m ) {
cout << "User " << who << " dials in at time "
<< time << endl;
if( m.freeModems > 0 ) {
m.freeModems--;
int howLong = r.negExp( m.avgCallLen );
cout << " and connects for " << howLong <<
" minutes " << endl;
time += howLong;
return true;
}
else {
cout << "but gets busy signal" << endl;
return false;
}
}
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++++ Hangup +++++++++++++++++++++++++++++
//inhereited constructor
Hangup::Hangup (int name, int tm )
: time( tm ), who( name ) {
return;
}
//copy constructor
Hangup::Hangup ( const Hangup &h ) {
*this = h;
}
//destructor
Hangup::~Hangup ( ) {
}
//bool process - unfinished
bool Hangup::process( ModemSimV2 &m ) {
m.freeModems++;
cout << "User " << who << " hangs up at time "
<< time << endl;
return false;
}
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ EventHeap ++++++++++++++++++++++++++++
//EventHeap constructor
EventHeap::EventHeap( ) {
array = new Event*[1];
size = 0;
}
//constructor
EventHeap::EventHeap( int numVals ) {
array = new Event*[numVals];
size = 0;
}
//push function takes a pointer to an Event and puts into the array
void EventHeap::push( Event *e ) {
array[size] = e;
reIndex( size );
size++;
}
//since this is a min-heap, it removes the smallest value in the array
Event* EventHeap::pop( ) {
Event *e = array[0];
array[0] = array[size - 1];
size--;
if( !empty( ) )
buildHeap(0);
return e;
}
//builds the heap once popped, to reorder the array
//
void EventHeap::buildHeap( int index ) {
int min;
if (getRight(index) >= size) {
if (getLeft(index) >= size)
return;
else
min = getLeft(index);
}
else {
if (array[getLeft(index)] <= array[getRight(index)])
min = getLeft(index);
else
min = getRight(index);
}
if (array[index] != 0 && array[index] > array[min]) {
Event *temp( array[min] );
array[min] = array[index];
array[index] = temp;
buildHeap(min);
}
}
//similar to buildHeap, but is called when a value is pushed to the array
//
void EventHeap::reIndex( int hole ) {
while( array[hole] != NULL && array[hole] < array[getParent( hole )] ) {
int pIndex = getParent( hole );
Event *temp( array[hole] );
array[hole] = array[pIndex];
array[pIndex] = temp;
hole = pIndex;
}
}
//returns true if empty
bool EventHeap::empty() const {
return ( size == 0 );
}
//returns the left child
int EventHeap::getLeft( int index ) const {
return ( index * 2 ) + 1;
}
//returns the right child
int EventHeap::getRight( int index ) const {
return ( index * 2 ) + 2;
}
//returns the parent
int EventHeap::getParent( int index ) const {
return ( index - 1 ) / 2;
}
Header:
// ModemSimV2.h
//
// #author David Harrigan
// dtk24
//
// 4/10/2012
//
#ifndef MODEM_SIM_V2_H
#define MODEM_SIM_V2_H
#include <queue>
#include <vector>
#include <functional> // for greater()
#include <climits> // for INT_MAX
#include <iostream>
#include "random.h"
using namespace std;
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ ModemSimV2 +++++++++++++++++++++++++++
class EventHeap;
class ModemSimV2 {
public:
ModemSimV2( int modems, double avgLen, int callIntrvl );
// Add a call to eventSet at the current time,
// and schedule one for delta in the future.
void nextCall( int delta );
// Run the simulation
void runSim( int stoppingTime );// = INT_MAX );
friend class Event;
friend class Dialin;
friend class Hangup;
private:
Random r; // A random source
EventHeap *eventSet; // Pending events
// Basic parameters of the simulation
int freeModems; // Number of modems unused
const double avgCallLen; // Length of a call
const int freqOfCalls; // Interval between calls
};
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Event +++++++++++++++++++++++++++++
class Event{
protected:
int who; // the number of the user
int time; // when the event will occur
friend class ModemSimV2;
Random r;
public:
Event( );
Event( const Event &e );
virtual ~Event( );
bool operator > ( const Event & rhs ) const;
bool operator < ( const Event & rhs ) const;
bool operator <= ( const Event & rhs ) const;
bool operator != ( const Event & rhs ) const;
int getTime( ) { return time; };
virtual bool process( ModemSimV2 &m ) = 0;
};
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Dialin ++++++++++++++++++++++++++++
class Dialin : public Event {
public:
Dialin( int name = 0, int tm = 0 );
Dialin( const Dialin &d );
~Dialin( );
virtual bool process( ModemSimV2 &m );
private:
int who;
int time;
};
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//+++++++++++++++++++++++++++++ Hangup ++++++++++++++++++++++++++++
class Hangup : public Event {
public:
Hangup( int name = 0, int tm = 0 );
Hangup( const Hangup &h );
~Hangup( );
virtual bool process( ModemSimV2 &m );
private:
int who;
int time;
};
//xvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvxvx
//++++++++++++++++++++++++++ EventHeap ++++++++++++++++++++++++++++
class EventHeap{
public:
EventHeap();
EventHeap( int numIndex );
bool empty( ) const;
const int & findMin( ) const;
void push( Event *x );
Event * pop();
private:
int size; // Number of elements in heap
Event **array; // The heap array
void buildHeap( int index );
void reIndex( int hole );
int getLeft( int index ) const;
int getRight( int index )const;
int getParent( int index )const;
};
#endif
Here is a clue:
e = new Dialin( userNum++, nextCallTime );
eventSet->push( *e );
You are allocating a new object and pushing a copy of it.
Another clue:
// Constructor for ModemSim.
ModemSimV2::ModemSimV2( int modems, double avgLen, int callIntrvl )
:
r( (int) time( 0 ) ),
// what about eventSet?
freeModems( modems ),
avgCallLen( avgLen ),
freqOfCalls( callIntrvl ),
{
nextCall( freqOfCalls ); // Schedule first call
}
eventSet is declared as an EventHeap* in ModemSimV2, however you do not initialize it in the constructor. Therefore when you first use it a segfault occurs.
ModemSimV2::ModemSimV2( int modems, double avgLen, int callIntrvl )
: freeModems( modems ), avgCallLen( avgLen ),
freqOfCalls( callIntrvl ), r( (int) time( 0 ) )
{
eventSet = new EventHeap(); // <-- missing this initialization
nextCall( freqOfCalls ); // Schedule first call
}
Also to avoid memory leaks, you will need a destructors in ModemSimV2 and EventHeap to free the memory you allocate.