• 16839阅读
  • 9回复

创建树(继承自QAbstractItemModel) [复制链接]

上一主题 下一主题
离线wvins
 
只看楼主 倒序阅读 楼主  发表于: 2008-11-07
— 本帖被 XChinux 从 General Qt Programming 移动到本区(2011-01-02) —
继续向RSS阅读器迈进。
创建一个TreeModel,参考并修改自SimpleTreeModel例子
------------
这个例子我没有完全搞明白数是怎么创建的,
我猜想相当于接口约束的方式,规范了QAbstractItemModel必须要实现Index(row,column,parent)方法。
QTreeModel获取(0,0,QModelIndex())位置的QModelIndex也就是根结点。其中QModelIndex是个无效的Index。
然后通过data(index,role)获得index指定位置的指定角色的数据。比如说我关注的图标数据
flags方法和headerData方法我不关心
===========
然后剩下就是广度有限或者深度优先创建树的展现。
    通过rowCount(parent)获取当前Index的子节点数
    通过ColumnCount(parent)获取当前Index的字节点的列数。
    通过Index(row,Column,parent)获得指定的节点index
    通过data(index,role)获得指定节点的数据
===========
我的树建立在Sqlite基础上,目前尚未解决编码问题,有高手指点一定事半功倍,不胜感激。
我的数据是通过SqlBrowser插入的,通过Qt读出后,应该是编码的问题,看不到标题。
我想问QString有没有办法转成GB2312。
我对编码不熟,如果能给讲讲Qt中的字符编码就更好了。  
貌似Qt对查询得到的结果进行了正确的编码,
看不到标题完全是代码写错了 
=================
下面贴代码:
TreeItem.h和TreeItem.cpp原封不动
TreeModel.h和TreeModel.cpp涉及到创建树的方法被替换成通过Strategy模式创建,代码如下:
TreeModel.h
#ifndef TREEMODEL_H
#define TREEMODEL_H
 
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
#include <QSqlQuery>
#include <QMessageBox>
 
class TreeItem;
 
class TreeStrategy
{
public:
    virtual ~TreeStrategy(){};
    virtual void buildTree(TreeItem *root){QMessageBox::information(0,"create","create");};
};

class TreeModel : public QAbstractItemModel
{
    Q_OBJECT
public:
    TreeModel(TreeStrategy *treeStrategy, QObject *parent = 0);
    ~TreeModel();
 
    QVariant data(const QModelIndex &index, int role) const;
    Qt::ItemFlags flags(const QModelIndex &index) const;
    QVariant headerData(int section, Qt::Orientation orientation,
                        int role = Qt::DisplayRole) const;
    QModelIndex index(int row, int column,
                      const QModelIndex &parent = QModelIndex()) const;
    QModelIndex parent(const QModelIndex &index) const;
    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;
private:
    TreeItem *rootItem;
};
#endif
TreeModel.cpp
#include <QtGui>
#include <QMessageBox>
 
#include "treeitem.h"
#include "treemodel.h"
 
TreeModel::TreeModel(TreeStrategy *treeStrategy, QObject *parent)
    : QAbstractItemModel(parent)
{
    QList<QVariant> rootData;
    rootData << tr("频道列表");
    rootItem = new TreeItem(rootData);
    treeStrategy->buildTree(rootItem);
    //对于C++中关于指针,引用,对象的多态忘光了
    //在Delphi中所有的对象都是引用,省掉了很多麻烦
}
 
TreeModel::~TreeModel()
{
    delete rootItem;
}
 
int TreeModel::columnCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
    else
        return rootItem->columnCount();
}
 
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
/*    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

    TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

    return item->data(index.column());*/
    if (!index.isValid())
        return QVariant();
    TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
    switch(role)
    {
    case Qt::DisplayRole:
    {
        return item->data(index.column());
        break;
    }
    case Qt::DecorationRole:
    {
        QPixmap pixmap;
        pixmap.loadFromData(item->data(1).toByteArray());
        return pixmap;
    }
    default: return QVariant();
    }
}
 
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;
 
    return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
 
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                               int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
        return rootItem->data(section);
 
    return QVariant();
}
 
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
            const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();
 
    TreeItem *parentItem;
 
    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());
 
    TreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}
 
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();
 
    TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
    TreeItem *parentItem = childItem->parent();
 
    if (parentItem == rootItem)
        return QModelIndex();
 
    return createIndex(parentItem->row(), 0, parentItem);
}
 
int TreeModel::rowCount(const QModelIndex &parent) const
{
    TreeItem *parentItem;
    if (parent.column() > 0)
        return 0;
 
    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());
 
    return parentItem->childCount();
}
其中紫色部分是修改过得代码。
[ 此贴被wvins在2008-11-09 20:19重新编辑 ]
离线wvins
只看该作者 1楼 发表于: 2008-11-07
代码太多了,不过上面黑色部分都是原封不动的SimpleTreeModel代码,修改后仅需要按照规则生成TreeItem树就行了。
RSSTreeBuilder.h
#ifndef RSSTREEBUILDER_H
#define RSSTREEBUILDER_H
#include "treeitem.h"
#include "treemodel.h"
 
#include <QSqlDatabase>
#include <QSqlQuery>
 
class RSSTreeBuilder: public TreeStrategy
{
public:
    RSSTreeBuilder(const QSqlDatabase &db = QSqlDatabase());
    ~RSSTreeBuilder();
    void buildTree(TreeItem *root);
private:
    QSqlQuery * qryGroup, * qryChannel;
};
#endif
RSSTreeBuilder.cpp
#include "RSSTreeBuilder.h"
#include <QSqlQuery>
#include <QList>
#include <QVariant>
#include <QSqlRecord>
#include <QSqlField>
#include <QObject>
#include <QMessageBox>
 
RSSTreeBuilder::RSSTreeBuilder(const QSqlDatabase &db)
{
    qryGroup = new QSqlQuery(db);
    qryChannel = new QSqlQuery(db);
}
 
RSSTreeBuilder::~RSSTreeBuilder()
{
    delete qryGroup;
    delete qryChannel;
}
 
void RSSTreeBuilder::buildTree(TreeItem *root)
{
    qryGroup->exec("select * from RSSGroups where GroupType = 'gtOnline'");
    qryChannel->exec("select * from RSSChannels");
    QList<QVariant> itemData;
    itemData << QObject::tr("我订阅的新闻");
    TreeItem * level1 = new TreeItem(itemData, root);
    root->appendChild(level1);
    do
    {
        itemData.clear();
        itemData << qryGroup->record().field("GroupName").value()
                 //<< qryGroup->record().field("icon").value()
                 ;
        TreeItem * level2 = new TreeItem(itemData, level1);
        level1->appendChild(level2);
        do
        {
            if(qryChannel->record().field("GroupID") != qryGroup->record().field("GroupID")) continue;
            itemData.clear();
            itemData << qryChannel->record().field("Title").value()
                     << qryChannel->record().field("icon").value();
            TreeItem * level3 = new TreeItem(itemData, level2);
            level2->appendChild(level3);
        }while(qryChannel->next());
    }while(qryGroup->next());
    //红色部分为错误的消息循环,正确的循环见下面蓝色循环
 
    itemData.clear();
    itemData << QObject::tr("我搜集的新闻");
    level1 = new TreeItem(itemData, root);
    root->appendChild(level1);
    qryGroup->exec("select * from RSSGroups where GroupType = 'gtOffline'");
    while(qryGroup->next())
    {
        itemData.clear();
        itemData << qryGroup->record().field("GroupName").value()
        //目前从数据库取出的字符串还不能正确识别,望高手指点
                 //<< qryGroup->record().field("icon")
                 ;
        TreeItem * level2 = new TreeItem(itemData, level1);
        level1->appendChild(level2);
        while(qryChannel->next())
        {
            if(qryChannel->record().field("GroupID") != qryGroup->record().field("GroupID")) continue;
            itemData.clear();
            itemData << qryChannel->record().field("Title").value()
                     << qryChannel->record().field("icon").value();
            TreeItem * level3 = new TreeItem(itemData, level2);
            level2->appendChild(level3);
        }
    }
}
蓝色部分为证确的循环,因为QSqlQuery.exec()执行查询之后,没有定位到第一条记录,而是定位到了一条非法记录,所以这个循环是正确的。
修改后能够正确创建树的源代码 12.TreeModel.tar.gz (68 K) 下载次数:52
[ 此贴被wvins在2008-11-09 20:20重新编辑 ]
描述:完整源代码和Sqlite数据库
附件: 12.TreeModel.tar.gz (3 K) 下载次数:76
离线lazybone
只看该作者 2楼 发表于: 2008-11-08
支持原创 虽然看不懂
离线wvins
只看该作者 3楼 发表于: 2008-11-08
谢谢支持。 
我想应该把我想达到的效果告诉大家,会比较好理解一些。
我想实现下面这棵树:
频道列表
    我订阅的新闻
        RSSGroups中的gtOnline的分组
            RSSChannel中属于该分组的订阅
    我搜集的新闻
        RSSGroups中的gtOffline的分组
            RSSChannel中属于该分组的订阅

树的根结点自动变成标题了,这不是我希望的...

下一步:
我希望能够在这个基础上增加图标的支持

代码写的不好,希望大家指点指点,让我知道。
我真心希望写好C++,和大家共同进步
[ 此贴被wvins在2008-11-08 09:44重新编辑 ]
离线wvins
只看该作者 4楼 发表于: 2008-11-09
折腾了一天,终于插入图标到数据库,并且正确展现到树里面了。

     
主要是修改了原帖中的TreeModel.cpp下的
QVariant TreeModel::data(const QModelIndex &index, int role) const方法。
------
我的树比较简单,只有一列数据,但是仍旧沿用了QList为每个节点存放数据。
目前,QList[0]存放的是Qt::DisplayText的数据,也就是显示用的文本
QList[1]存放的是Qt::DecorationRole的数据,也就是显示用的图标

继续在原帖上修改。
紫色标识
能够显示图标的源代码和数据库文件 12.TreeModel.tar.gz (36 K) 下载次数:90
[ 此贴被wvins在2008-11-09 20:23重新编辑 ]
离线amaorn

只看该作者 5楼 发表于: 2008-11-13
qtreeWidget里能添加QWidget窗体吗?我要在树型结构中加入很多不同的QWidget的窗体,请问怎么实现?
离线water_wf

只看该作者 6楼 发表于: 2008-11-13
tree->setIndexWidget ( const QModelIndex &, QWidget * )
离线yanqixiao

只看该作者 7楼 发表于: 2009-04-22
楼主问个问题:QAbstractItemModel中的虚函数,是怎么被调用的呢?例如index(),parent();demo中的treemodel,没见有调用,但是都执行了,应该是内部调用吧?我对这块理解不好,请多多指教。
彪悍不需要理由
离线wvins
只看该作者 8楼 发表于: 2009-04-22
你需要什么层面上的回答呢?
如果你只是想知道为什么会被调用呢,我可以告诉你。
这一块是面向对象的基础,通过对父类引用调用虚函数,最终会调用派生类的方法。
---------------
如果你要问是什么时候调用的,那就要看源代码了,我只能凭感觉猜测
这个我记得好像在那篇帖子里面说过...

首先会通过QModelIndex()生成一个无效节点,通常这个就是根节点,对任何Model都是。
然后基于这个根节点,通过index函数能够遍历所有的子节点。

headerData(),data()等函数是在显示需要取数的时候调用的,
parent()什么时候调用的,不清楚,我猜测它只是作为一个虚函数提供给程序员使用的。
-------------------
就是这些了
离线郝霞的
只看该作者 9楼 发表于: 2012-05-29
楼主,想请教个问题,TreeItem我想实现一个动态的增长,就是这个树的大小是不固定的饿,当有信息来时会自动增加一个TreeItem,,这个问题困扰我好久了
快速回复
限100 字节
 
上一个 下一个