PDA

View Full Version : Model / View and data structures



laszlo.gosztola
2nd December 2010, 05:13
Hi all!

I am using Qt for a while and now I try to improve my skills. I would like to take a closer look on Model/View concept and QML too.

My idea is to create an 'Advanced TODO list'
I plan to define the user interface in qml, and the application logic in c++ on a way that I have a mainwindow with QDeclarativeView and I load the Main.qml into it.

My concept for the applicaton:
The program has two main page: Calendar view and daily view.
On calendar view there is a calendar, e.g. the day which has task with bold. Maybe most important task to a day is visible also here.
If I click on a day the daily view comes up, and I can organize the day.
I can assign a rating to a day - how succesful the day was. It should be visible on the calendar view.
I assign an image also to the day - It's like an icon - if it has been set it is visible also in calendar view.

But I am unsure the usage and the data structures I should use.

Now I have created on C++ side a MonthModel. It focuses only on the calendar part - the detailed daily things not defined yet.

My question is:
Is this approach seems ok? I wasn't sure if a Table Model should be used with columns rating, title, etc. or this listmodel with DayData class.
On QML side - I create a gridview. Put some empty rectangles into it based on monthModel spaceBefore property. After that fill up the days, and put some other empty rectangles (spaceAfter)
Is it OK, or could I do it in more elegant way?

And the most important question I think:
For the detailed data - let's assume I would assign images and rich texts to a day. Should I use a new model or is it possible to extend it?
My idea is to extend DayData class with e.g. with variables QList<QString> and QList<QImage> and they can store texts and images. But is it possible to use these lists like an other model on the qml side to display the data?

Sorry for the long post.

Thanks!

monthmodel.h



class DayData
{
public:
DayData(const QString& title, short rating, const QImage& img);

QString title() const;
short rating() const;
QImage image() const;

private:

QString m_title;
short m_rating;
QImage m_image;
};


class MonthModel : public QAbstractListModel
{
Q_OBJECT
public:
enum MonthRoles {
TitleRole = Qt::UserRole + 1,
RatingRole,
ImageRole
};

MonthModel(QObject *parent = NULL);
~MonthModel();

Q_PROPERTY (short year READ year NOTIFY yearChanged);
Q_PROPERTY (short month READ month NOTIFY monthChanged);
Q_PROPERTY (QString monthName READ monthName NOTIFY monthNameChanged);
Q_PROPERTY (short spaceBefore READ spaceBefore NOTIFY spaceBeforeChanged);
Q_PROPERTY (short spaceAfter READ spaceAfter NOTIFY spaceAfterChanged);

//void setYearMonth(short year, short month);
int rowCount(const QModelIndex &parent = QModelIndex()) const;

QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

public slots:
void previousMonth();
void nextMonth();

signals:
void spaceBeforeChanged();
void spaceAfterChanged();
void monthChanged();
void monthNameChanged();
void yearChanged();


private:
short spaceBefore();
short spaceAfter();
short month() {return m_month;}
short year() {return m_year;}
QString monthName();

void fillData();
QList<DayData> m_days;
short m_year;
short m_month;

};


monthmodel.cpp


#include "monthmodel.h"

#include <QDate>


DayData::DayData(const QString &title, short rating, const QImage &img) :
m_title(title), m_rating(rating), m_image(img)
{
}


MonthModel::MonthModel(QObject *parent) : QAbstractListModel(parent)
{
m_days.clear();
m_year=QDate::currentDate().year();
m_month=QDate::currentDate().month();

QHash<int, QByteArray> roles;
roles[TitleRole] = "title";
roles[RatingRole] = "rating";
roles[ImageRole] = "image";
setRoleNames(roles);

fillData();
}

MonthModel::~MonthModel()
{
}

QVariant MonthModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if ((index.row()>=m_days.size()) || (index.row()<0))
return QVariant();

const DayData& day=m_days.at(index.row());

if (role == TitleRole)
{
return day.title();
}else if (role==RatingRole)
{
return day.rating();
}else if (role==ImageRole)
{
return day.image();
}
return QVariant();
}

short MonthModel::spaceBefore()
{
short r=QDate(m_year, m_month, 1).dayOfWeek()-1;
qDebug("MonthModel::spacebefore - %d", r);
return (r);
}

short MonthModel::spaceAfter()
{
QDate firstDay=QDate(m_year, m_month, 1);
short r=7-firstDay.addDays(firstDay.daysInMonth()-1).dayOfWeek();
qDebug("MonthModel::spaceAfter - %d", r);
return r;
}

int MonthModel::rowCount(const QModelIndex &parent) const
{
return m_days.count();
}

QVariant MonthModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role!= Qt::DisplayRole)
return QVariant();
return QString("NotYetDefined");
}


void MonthModel::fillData()
{
beginResetModel();

m_days.clear();

//beginInsertRows(QModelIndex(), rowCount(), rowCount());
for (int i=0; i<QDate(m_year, m_month, 1).daysInMonth(); i++)
{
m_days << DayData(QString("%1").arg(i+1), 0, QImage());
}
endResetModel();
//endInsertRows();
qDebug("RowCount: %d", rowCount());
emit spaceBeforeChanged();
emit spaceAfterChanged();

}

void MonthModel::previousMonth()
{
if (m_month!=1)
{
m_month--;
emit monthChanged();
emit monthNameChanged();
}else
{
m_year--;
m_month=12;
emit monthChanged();
emit monthNameChanged();
emit yearChanged();
}
fillData();

}

void MonthModel::nextMonth()
{
if (m_month!=12)
{
m_month++;
emit monthChanged();
emit monthNameChanged();
}else
{
m_year++;
m_month=1;
emit monthChanged();
emit monthNameChanged();
emit yearChanged();
}
fillData();
}

QString MonthModel::monthName()
{
return QDate::longMonthName(m_month, QDate::StandaloneFormat);
}



Calendar.qml


import Qt 4.7

Item {
id: calendar
width:500
height: 420

Image {
id: background
anchors.fill: parent
source:"./pics/calendar_sablon.png"
smooth: true
fillMode: Image.PreserveAspectFit
}

Button {
id: prev
anchors.left: parent.left
anchors.leftMargin: 10
y: parent.height*0.05
width: 40
imagesource: "./pics/yellowstone.png"
label:"Prev"

Behavior on width {
NumberAnimation { duration: 100}
}

Behavior on rotation {
NumberAnimation {duration: 100}
}

onClicked: {
state=='clicked' ? state="" : state='clicked'
console.log("Clcked");
//monthModel.proba();

monthModel.previousMonth();
console.log(monthModel.length);
//console.log(monthModel.def)
}

/*monthLoader.source="Calendar.qml"
monthLoader.item.x=100
monthLoader.item.y=100*/
states: [
State {
name: 'clicked'

PropertyChanges {target: prev; width: 30; rotation: 180}
//PropertyChanges {target: monthModel; def: "YXCV"}
}

]
}

Text {
id: month
anchors.horizontalCenter: parent.horizontalCenter
y: parent.height*0.05
text: monthModel.year + ". "+ monthModel.monthName
//text: monthModel.def
color: "black"
font.strikeout: false
font.pointSize: 36
style: Text.Normal
font.family: "Monotype Corsiva"
smooth: true
opacity: 1
clip: false
}

Component {
id: dayRect
Rectangle {
width: 55
height: 55
opacity: 0.8
color: "lightgreen"
}
}

Component {
id: dayDelegate
Rectangle {
//color: if (title < 4){ "yellow"} else {"orange"}
color: "cyan"
width: 55
height: 55
opacity: 0.8

Text {
id: dummy
text: title + "-" +rating
smooth: true
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.opacity=1.0
parent.scale= 1.2
}
onExited: {
parent.opacity= 0.8
parent.scale=1
}
onDoubleClicked: {
console.log("doubleClicked")
console.log(index)
}
}

}
}

Grid {
z: 100
x: 5; y: 137

rows: 6; columns: 7; spacing: 6
//fill spaces before
Repeater {
model: monthModel.spaceBefore
delegate: dayRect
}

//the valid days
Repeater {
model: monthModel
delegate: dayDelegate
}

//fill spaces after
Repeater {
model: monthModel.spaceAfter
delegate: dayRect
}

}

}