zoukankan      html  css  js  c++  java
  • Qt Model View 框架

    Model-View及Qt实现

    Model-View-Controller架构最早出现在SmallTalk语言中,至今出现了很多变体。

    Model是负责维护数据(如管理数据库),View负责显示与用户交互(如各种界面),Controller将控制业务逻辑。这种分层的做法在大型程序中使得数据、逻辑与界面分离,便于维护更新。

    Qt引入了与MVC架构相似的模式Model-View架构,并加入了代理(delegate),用于自定义数据的编辑和渲染。

    因为架构中的Model以表格的抽象方式访问数据,事实上并非Model-View的最佳选择。

    Qt中Model,View,Delegate均由抽象类定义,并通过信号槽进行交互:

    • Model的信号通知View数据发生了改变

    • View的信号通知用户交互事件

    • Delegate的信号在编辑数据时用于通知Model和View的状态

    QAbstractItemModel是所有Model的基类,它定义了View和Delegate访问数据的接口。

    模型并不存储数据,而是通过与数据源交互得到数据。数据源包括数据库,文件,内存中的对象以及IO设备。

    Qt 内置了许多标准模型:

    • QStringListModel:存储简单的字符串列表。

    • QStandardItemModel:可以用于树结构的存储,提供了层次数据。

    • QFileSystemModel:本地系统的文件和目录信息。

    • QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel:存取数据库数据。

    如果这些标准模型不能满足需要,可以继承QAbstractItemModel创建新的Model。

    QAbstractListModel或QAbstractTableModel提供了一些基本的实现,继承它们可能是更好的选择。

    QAbstractItemView是所有View的基类。Qt 还提供了一系列标准的视图:QListView用于显示列表,QTableView用于显示表格,QTreeView用于显示层次数据,它们与List,Table, Tree这些布局在一定程度上对应。

    QAbstractItemDelegate则是所有委托的抽象基类。自 Qt 4.4 之后,默认的委托实现是QStyledItemDelegate。但是,QStyledItemDelegate和QItemDelegate都可以作为视图的编辑器,二者的区别在于,QStyledItemDelegate使用当前样式进行绘制。在实现自定义委托时,推荐使用QStyledItemDelegate作为基类,或者结合 Qt style sheets。

    QListWidget,QtreeWidget,QTableWidget

    基于MVC架构,Qt提供了QListWidget,QtreeWidget,QTableWidget三个可视化组件,它们均继承了相应的View类,集成了Model-View的功能,程序员可以方便地使用这些类进行开发标准的List、Tree和Table组件。

    QListWidget

    QListWidget用于显示列表,QListWidget的创建方法与其它Widget一样需要指定一个父组件QListWidget(QWidget * parent = 0)

    QListWidget的列表项是QListWidgetItem对象,QListWidgetItem可以存储和显示图标及文本。

    在建立QListWidgetItem 对象时指定一个QListWidget对象作为父对象即可将列表项添加到列表组件最后,调用 void addItem(QListWidgetItem * item)实例方法也可以将列表项添加到列表尾。

    void insertItem(int row, QListWidgetItem * item);系列重载函数可以将菜单项添加到指定的位置。

    void insertItems(int row, const QStringList & labels)void addItems(const QStringList & labels)方法则可以批量添加列表项。

    QListWidgetItem可以在创建对象的时候指定图标和文本。

    QListWidgetItem(const QIcon & icon, const QString & text, QListWidget * parent = 0, int type = Type),也可以通过setIcon(const QIcon & icon)setText(const QString & text)设置。

    对于Item的显示风格有很多选择,详情可以查阅Qt文档。

    QListWidget继承了QListView的setViewMode函数可以设置列表的显示风格。QListWidget定义了一系列信号,它们会在列表项被点击等事件发生时发出。

    QTreeWidget

    QTreeWidget用于显示树状组件,其用法与QListWidget非常相似,它的项是QTreeWidgetItem对象。

    QTreeWidgetItem有非常多的重载构造函数,QTreeWidgetItem可以接受QTreeWidget或QTreeWidget作为父对象,由此可以创建树状结构。

    QTreeWidget和QTreeWidgetItem中常使用QList来创建多根树。

    QTreeWidget可以用来显示类似Windows资源管理器的界面。

    void setColumnCount(int columns)可以设置列数目,而QTreeWidget的使用QStringList存储文本也是为了存储多列数据。

    void setHeaderLabels(const QStringList & labels)可以设定列名称。

    QTableWidgets

    QTableWidget的用法与前两个相似,其项目类为QTableWidgetItem。初始化QTableWidget对象时需要先指定对象的宽和高,使用void setItem(int row, int column, QTableWidgetItem * item)将项目添加到指定单元格

    Model

    标准Model

    QStringListModel

    QStringListModel是最简单的模型类,具备向视图提供字符串数据的能力。QStringListModel是一个可编辑的模型,可以为组件提供一系列字符串作为数据,可以将其看作是封装了QStringList的模型。

    QStringListModel很多时候都会作为QListView或者QComboBox这种只有一列的视图组件的数据模型。

    void setStringList(const QStringList & strings);用于设置QStringListModel所维护的StringList,使用View的void setModel(QAbstractItemModel * model)函数将View与Model关联。

    QFileSystemModel

    QFileSystemModel的作用是维护一个目录的信息。因此,它不需要保存数据本身,而是保存这些在本地文件系统中的实际数据的一个索引。我们可以利用QFileSystemModel访问文件系统信息、甚至通过模型来修改文件系统。QTreeView是最适合应用QFileSystemModel的视图。

    model = new QFileSystemModel;
    model->setRootPath(QDir::currentPath());
    
    treeView = new QTreeView(this);
    treeView->setModel(model);
    treeView->setRootIndex(model->index(QDir::currentPath()));
    

    自定义Model

    QAbstractItemModel定义了Model的标准接口。QAbstractItemModel及其派生类均以表格的形式提供访问数据。

    自定义Model需要继承QAbstractItemModel并重写下列函数:

    • data()

      QVariant QAbstractItemModel::data(const QModelIndex & index,
      int role = Qt::DisplayRole) const

    访问数据的接口,QModelIndex是存储Model表格的索引,index.row()index.column()可以得到索引中指向的行或列。

    role是一个枚举代表了数据的渲染方式,QVariant是变体型可以被转换为任意Qt兼容的数据类型。

    • setData()

      bool QAbstractItemModel::setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole)

    写入数据的接口。

    • dataChanged信号

      void QAbstractItemModel::dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, const QVector & roles = QVector ())

    在数据被改变后由setData()方法发送dataChanged()信号,通知视图刷新数据。使用两个QModelIndex通知刷新的范围。

    此外还有一些工具函数:

    • rowCount() / columnCount()

    返回模型的行数 / 列数。

    • headerData()

    返回表头信息。

    示例:

    显示汇率表的应用,底层的数据使用一个QMap<QString, double>类型的数据,作为key的QString是货币名字,作为value的double是这种货币对美元的汇率

    CurrencyModel.h

    class CurrencyModel : public QAbstractTableModel 
    { 
    	Q_OBJECT
    public: 
            CurrencyModel(QObject *parent = 0); 
            void setCurrencyMap(const QMap<QString, double> &map); 
            int rowCount(const QModelIndex &parent) const; 
            int columnCount(const QModelIndex &parent) const; 
            QVariant data(const QModelIndex &index, int role) const; 
    		bool setData(const QModelIndex &index, const QVariant &value, int role); 
            QVariant headerData(int section, Qt::Orientation orientation, int role) const; 
    private: 
            QString currencyAt(int offset) const; 
            QMap<QString, double> currencyMap; 
    };
    

    CurrencyModel.cpp

    CurrencyModel::CurrencyModel(QObject *parent) 
        : QAbstractTableModel(parent) 
    { 
    } 
     
    int CurrencyModel::rowCount(const QModelIndex & parent) const 
    { 
            return currencyMap.count(); 
    } 
     
    int CurrencyModel::columnCount(const QModelIndex & parent) const 
    { 
            return currencyMap.count(); 
    } 
     
    QVariant CurrencyModel::data(const QModelIndex &index, int role) const 
    { 
            if (!index.isValid()) 
                    return QVariant(); 
     
            if (role == Qt::TextAlignmentRole) { 
                    return int(Qt::AlignRight | Qt::AlignVCenter); 
            } 
    		else if (role == Qt::DisplayRole) { 
                    QString rowCurrency = currencyAt(index.row()); 
                    QString columnCurrency = currencyAt(index.column()); 
                    if (currencyMap.value(rowCurrency) == 0.0) 
                            return "####"; 
                    double amount = currencyMap.value(columnCurrency) / currencyMap.value(rowCurrency);
                    return QString("%1").arg(amount, 0, 'f', 4); 
            } 
            return QVariant(); 
    }
    
    bool CityModel::setData(const QModelIndex &index, const QVariant &value, int role) 
    { 
    
    	if (!index.isValid() || role != Qt::EditRole) {
    		return false;
    	}
        if ( currencyAt(index.column()) == "USD") { 
    			QString currency = currencyAt(index.row());
    			currencyMap[currency] = toInt(value);
    
                QModelIndex columnIndexBegin = createIndex(index.column(),0);
    			QModelIndex columnIndexEnd = createIndex(index.column(),currencyMap.count()- 1);
                emit dataChanged(columnIndexBegin, columnIndexEnd);
    
    			QModelIndex rawIndexBegin = createIndex(0,index.raw());
    			QModelIndex rawIndexEnd = createIndex(currencyMap.count()- 1,index.raw());
                emit dataChanged(rawIndexBegin, rawIndexEnd); 
                return true; 
        } 
        return false; 
    }  
     
    QVariant CurrencyModel::headerData(int section, Qt::Orientation orientation, int role) const 
    { 
            if (role != Qt::DisplayRole) 
                    return QVariant(); 
            return currencyAt(section); 
    } 
     
    void CurrencyModel::setCurrencyMap(const QMap<QString, double> &map) 
    { 
            currencyMap = map; 
            reset(); 
    } 
     
    QString CurrencyModel::currencyAt(int offset) const 
    { 
            return (currencyMap.begin() + offset).key(); 
    }
  • 相关阅读:
    wpf 资源
    九宫格扩展,输入一个奇数,得到横竖斜相加相等结果
    安装IIS的convlog.exe问题
    Windows API
    使用快捷键弹出新对话框
    数据结构笔试(转)
    运算符重载
    宽字节与多字节转换
    C++之乱七八糟<真正的随笔>
    MFC调用ActiveX
  • 原文地址:https://www.cnblogs.com/Finley/p/5285532.html
Copyright © 2011-2022 走看看