I've been trying to populate a tableview with dynamically created columns based on model headers (this works as intended now!) However I still can't seem to get the TableView to correctly read my model data. I checked to see if EmployeeTableModel::data() was ever called and it wasn't so thats a pretty big red flag.
My compiler errors are ReferenceError: score is not defined, etc for all user created roles. My EmployeeModelMaster works perfectly even though it has essentially the same class structure.
I think I'm missing something simple, the qml file will be attached below.
main.cpp
int main(int argc, char *argv[])
{
QQmlApplicationEngine engine;
EmployeeModelMaster* masterModel = new EmployeeModelMaster();
EmployeeModelTable* table = new EmployeeModelTable();
QObject::connect(masterModel,
SIGNAL(updateMirrors
(int,EmployeeModelMaster
*)), table,
SLOT(masterDataChanged
(int,EmployeeModelMaster
*)));
masterModel->configSQL();
qDebug()<<"info successfully pulled from database? " << masterModel->pullFromSQL();
engine.rootContext()->setContextProperty("baseTableModel", table);
engine.rootContext()->setContextProperty("headers", table->headerList());
qDebug()<<"right before loading shyftwrk.qml";
engine.
load(QUrl(QStringLiteral
("qrc:///qml/ShyftWrk.qml")));
qDebug()<<"engine loaded shyftwrk.qml";
return app.exec();
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QQmlApplicationEngine engine;
EmployeeModelMaster* masterModel = new EmployeeModelMaster();
EmployeeModelTable* table = new EmployeeModelTable();
QObject::connect(masterModel, SIGNAL(updateMirrors(int,EmployeeModelMaster*)), table, SLOT(masterDataChanged(int,EmployeeModelMaster*)));
masterModel->configSQL();
qDebug()<<"info successfully pulled from database? " << masterModel->pullFromSQL();
engine.rootContext()->setContextProperty("baseTableModel", table);
engine.rootContext()->setContextProperty("headers", table->headerList());
qDebug()<<"right before loading shyftwrk.qml";
engine.load(QUrl(QStringLiteral("qrc:///qml/ShyftWrk.qml")));
qDebug()<<"engine loaded shyftwrk.qml";
return app.exec();
}
To copy to clipboard, switch view to plain text mode
table.h
#ifndef EMPLOYEEMODELTABLE_H
#define EMPLOYEEMODELTABLE_H
/* this model is a mirror of EmployeeModelMaster, it is not intended to have data directly set; to update this table
* please connect the masterChanged() slot. */
{
Q_OBJECT
public:
typedef QList<QList<EmployeeData*> >::const_iterator const_iterator; //does this do anything?
explicit EmployeeModelTable
(QObject *parent
=0);
public:
enum EmployeeModelTableDataRole
{
nameRole=Qt::UserRole+1,
positionRole,
portraitRole,
scoreRole
};
public: //virtual inherited members from QAbstractTableModel
QVariant headerData
(int section, Qt
::Orientation orientation,
int role
) const;
bool setHeaderData
(int section, Qt
::Orientation orientation,
const QVariant &value,
int role
= Qt
::DisplayRole);
public: //new members specifically for our implementation
const_iterator begin()const{return m_data.begin();}
const_iterator end()const{return m_data.end();}
signals:
void headerDataChanged(int first, int last); //the only important signal from the tableModel
public slots:
void masterDataChanged(int rows, EmployeeModelMaster* master); //tells employeeModelTable to update, pulls from employeeModelMaster.
protected:
QHash<int, QByteArray> roleNames() const;
QList < EmployeeData
* > str_data;
public:
int headerItr;
};
#endif // EMPLOYEEMODELTABLE_H
#ifndef EMPLOYEEMODELTABLE_H
#define EMPLOYEEMODELTABLE_H
class EmployeeModelTable : public QAbstractTableModel
/* this model is a mirror of EmployeeModelMaster, it is not intended to have data directly set; to update this table
* please connect the masterChanged() slot. */
{
Q_OBJECT
Q_PROPERTY(QStringList headerList READ headerList)
public:
typedef QList<QList<EmployeeData*> >::const_iterator const_iterator; //does this do anything?
explicit EmployeeModelTable(QObject *parent=0);
public:
enum EmployeeModelTableDataRole
{
nameRole=Qt::UserRole+1,
positionRole,
portraitRole,
scoreRole
};
public: //virtual inherited members from QAbstractTableModel
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::DisplayRole);
Qt::ItemFlags flags(const QModelIndex &index) const;
public: //new members specifically for our implementation
bool newHeader(const QVariant &value);
QString name() const;
QStringList headerList();
const_iterator begin()const{return m_data.begin();}
const_iterator end()const{return m_data.end();}
signals:
void headerDataChanged(int first, int last); //the only important signal from the tableModel
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
public slots:
void masterDataChanged(int rows, EmployeeModelMaster* master); //tells employeeModelTable to update, pulls from employeeModelMaster.
protected:
QHash<int, QByteArray> roleNames() const;
QList < EmployeeData* > str_data;
QMap < int, QString > m_headerData;
public:
QList < QList < EmployeeData* > > m_data;
int headerItr;
};
#endif // EMPLOYEEMODELTABLE_H
To copy to clipboard, switch view to plain text mode
table.cpp
EmployeeModelTable
::EmployeeModelTable(QObject *parent
){
QList<EmployeeData*> null;
m_data.append(null);
headerItr =0;
}
int EmployeeModelTable
::rowCount(const QModelIndex &parent
) const {
Q_UNUSED(parent);
if(m_data[0].isEmpty())
return 0;
return m_data[0].count(); // since each column is identical, get the count from column 0
}
int EmployeeModelTable
::columnCount(const QModelIndex &parent
) const //all columns are identical {
Q_UNUSED(parent);
return m_data.count();
}
{
if(!index.isValid())
if(index.row() <0 || index.row()>= this->rowCount(index))
if(index.column() <0 || index.column()>= this->columnCount())
const EmployeeData *data = m_data[index.column()][index.row()];
if (role == nameRole)
return data->name();
else if (role == portraitRole)
return data->portrait();
else if(role == positionRole)
return data->position();
else if(role == scoreRole)
return data->score();
else
}
bool EmployeeModelTable
::setHeaderData(int section, Qt
::Orientation orientation,
const QVariant &value,
int role
) {
Q_UNUSED(orientation); //orientation will always be horizontal
if(role != Qt::DisplayRole)
return false;
for(int i=0; i <= m_headerData.count(); i++) //if the header already exists, don't make another one
{
if(m_headerData.value(i) == value)
return false;
}
m_headerData[section] = value.toString();
headerDataChanged(section, m_headerData.size());
return true;
}
bool EmployeeModelTable
::newHeader(const QVariant &value
) {
for(int i=0; i<m_headerData.size(); i++) //if the header already exists, don't make another one
{
if(!m_headerData.isEmpty())
{
if(m_headerData[i] == value.toString())
{
return false;
}
}
}
beginInsertColumns
(QModelIndex(), this
->columnCount
(), this
->columnCount
()+1);
m_headerData[headerItr] = value.toString();
m_data.append(str_data);
endInsertColumns();
headerItr++;
return true;
}
QVariant EmployeeModelTable
::headerData(int section, Qt
::Orientation orientation,
int role
) const {
Q_UNUSED(orientation); //orientation will always be horizontal
if(role != Qt::DisplayRole)
return m_headerData.value(section);
}
Qt
::ItemFlags EmployeeModelTable
::flags(const QModelIndex &index
) const{
if(index.isValid())
return QAbstractTableModel::flags(index
) | Qt
::ItemIsEditable | Qt
::ItemIsEnabled | Qt
::ItemIsSelectable;
return Qt::NoItemFlags;
}
QHash<int, QByteArray> EmployeeModelTable::roleNames() const
{
QHash<int, QByteArray> roles;
roles[nameRole] = "name";
roles[portraitRole] = "portrait";
roles[positionRole] = "position";
roles[scoreRole] = "score";
return roles;
}
void EmployeeModelTable::masterDataChanged(int rows, EmployeeModelMaster* master)
{
for(int k=0; k < this->columnCount(); k++)
{
while(rows > this->m_data[k].count()) // if the new row is bigger than the current, append with dummies
{
beginInsertRows
(QModelIndex(), rowCount
(), rowCount
()+1);
EmployeeData* nullperson = new EmployeeData();
this->m_data[k].append(nullperson);
this->str_data.append(nullperson);
endInsertRows();
}
for(int i=0; i < rows; i++)
{
if(i > m_data[k].count())
{
beginInsertRows
(QModelIndex(), rowCount
(), rowCount
()+1);
this->m_data[k].append(master->m_data[i]);
endInsertRows();
}
if(i > str_data.count())
{
this->str_data.append(master->m_data[i]);
}
this->m_data[k][i] = master->m_data[i]; //replace existing data in table with new data from master
endInsertRows();
this->str_data[i] = master->m_data[i];
newHeader(this->m_data[k][i]->position());
}
}
}
{
return m_headerData.values();
}
EmployeeModelTable::EmployeeModelTable(QObject *parent)
:QAbstractTableModel(parent)
{
QList<EmployeeData*> null;
m_data.append(null);
headerItr =0;
}
int EmployeeModelTable::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
if(m_data[0].isEmpty())
return 0;
return m_data[0].count(); // since each column is identical, get the count from column 0
}
int EmployeeModelTable::columnCount(const QModelIndex &parent) const //all columns are identical
{
Q_UNUSED(parent);
return m_data.count();
}
QVariant EmployeeModelTable::data(const QModelIndex &index, int role) const
{
if(!index.isValid())
return QVariant();
if(index.row() <0 || index.row()>= this->rowCount(index))
return QVariant();
if(index.column() <0 || index.column()>= this->columnCount())
return QVariant();
const EmployeeData *data = m_data[index.column()][index.row()];
if (role == nameRole)
return data->name();
else if (role == portraitRole)
return data->portrait();
else if(role == positionRole)
return data->position();
else if(role == scoreRole)
return data->score();
else
return QVariant();
}
bool EmployeeModelTable::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
{
Q_UNUSED(orientation); //orientation will always be horizontal
if(role != Qt::DisplayRole)
return false;
for(int i=0; i <= m_headerData.count(); i++) //if the header already exists, don't make another one
{
if(m_headerData.value(i) == value)
return false;
}
m_headerData[section] = value.toString();
headerDataChanged(section, m_headerData.size());
return true;
}
bool EmployeeModelTable::newHeader(const QVariant &value)
{
for(int i=0; i<m_headerData.size(); i++) //if the header already exists, don't make another one
{
if(!m_headerData.isEmpty())
{
if(m_headerData[i] == value.toString())
{
return false;
}
}
}
beginInsertColumns(QModelIndex(), this->columnCount(), this->columnCount()+1);
m_headerData[headerItr] = value.toString();
m_data.append(str_data);
endInsertColumns();
headerItr++;
return true;
}
QVariant EmployeeModelTable::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation); //orientation will always be horizontal
if(role != Qt::DisplayRole)
return QVariant();
return m_headerData.value(section);
}
Qt::ItemFlags EmployeeModelTable::flags(const QModelIndex &index) const
{
if(index.isValid())
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
return Qt::NoItemFlags;
}
QHash<int, QByteArray> EmployeeModelTable::roleNames() const
{
QHash<int, QByteArray> roles;
roles[nameRole] = "name";
roles[portraitRole] = "portrait";
roles[positionRole] = "position";
roles[scoreRole] = "score";
return roles;
}
void EmployeeModelTable::masterDataChanged(int rows, EmployeeModelMaster* master)
{
for(int k=0; k < this->columnCount(); k++)
{
while(rows > this->m_data[k].count()) // if the new row is bigger than the current, append with dummies
{
beginInsertRows(QModelIndex(), rowCount(), rowCount()+1);
EmployeeData* nullperson = new EmployeeData();
this->m_data[k].append(nullperson);
this->str_data.append(nullperson);
endInsertRows();
}
for(int i=0; i < rows; i++)
{
if(i > m_data[k].count())
{
beginInsertRows(QModelIndex(), rowCount(), rowCount()+1);
this->m_data[k].append(master->m_data[i]);
endInsertRows();
}
if(i > str_data.count())
{
this->str_data.append(master->m_data[i]);
}
beginInsertRows(QModelIndex(), 0, rowCount());
this->m_data[k][i] = master->m_data[i]; //replace existing data in table with new data from master
endInsertRows();
this->str_data[i] = master->m_data[i];
newHeader(this->m_data[k][i]->position());
}
}
}
QStringList EmployeeModelTable::headerList()
{
return m_headerData.values();
}
To copy to clipboard, switch view to plain text mode
qml snippet
import QtQuick 2.0
import QtQuick.Controls 1.2
Rectangle{
id: root
radius: 5
TableView{
id: schedulerTableView
itemDelegate: tableDelegate
resources:
{
var headerData = headers
var temp = []
for(var i=0; i<headerData.length; i++)
{
var role = headerData[i]
temp.push(columnComponent.createObject(schedulerTableView, { "role": role, "title": role}))
console.log("column #" + i + " is named : " + role);
}
return temp
}
model: baseTableModel
anchors.fill: root
}
Component
{
id: columnComponent
TableViewColumn{width: 220 }
}
Component{
id: tableDelegate
Rectangle{
id: delRectangle
height: 250
z:0
Clickable{
id: portraitText
source: portrait
smooth: true
antialiasing: true
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
height: 150
fillMode: Image.PreserveAspectFit
overlayOpacity: 0.4
}
Text{
id: nameText
text: name
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: portraitText.bottom
}
Text{
id: positionText
anchors.top: nameText.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: position
}
Text{
id:scoreText
anchors.top: positionText.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: score
}
}
}
}
import QtQuick 2.0
import QtQuick.Controls 1.2
Rectangle{
id: root
radius: 5
TableView{
id: schedulerTableView
itemDelegate: tableDelegate
resources:
{
var headerData = headers
var temp = []
for(var i=0; i<headerData.length; i++)
{
var role = headerData[i]
temp.push(columnComponent.createObject(schedulerTableView, { "role": role, "title": role}))
console.log("column #" + i + " is named : " + role);
}
return temp
}
model: baseTableModel
anchors.fill: root
}
Component
{
id: columnComponent
TableViewColumn{width: 220 }
}
Component{
id: tableDelegate
Rectangle{
id: delRectangle
height: 250
z:0
Clickable{
id: portraitText
source: portrait
smooth: true
antialiasing: true
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
height: 150
fillMode: Image.PreserveAspectFit
overlayOpacity: 0.4
}
Text{
id: nameText
text: name
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: portraitText.bottom
}
Text{
id: positionText
anchors.top: nameText.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: position
}
Text{
id:scoreText
anchors.top: positionText.bottom
anchors.horizontalCenter: parent.horizontalCenter
text: score
}
}
}
}
To copy to clipboard, switch view to plain text mode
Added after 21 minutes:
also I should mention, if I convert my TableView to a ListView, it populates with the first column from the model as intended, which makes me think I'm very close.
Added after 18 minutes:
I figured it out: If add the prefix "model." to each of my itemDelegate sources (image src, text text, etc) then it is read correctly. Not entirely sure why that's the case but I'm happy I figured it out.
Bookmarks