目录
前言
本篇介绍一下JanQuick_Demo的人员管理页面,主要是介绍TableView和QAbstractTableModel的使用,通过自定义model,delegate实现表格样式自定义和对表格的增删改查。展示的数据来源于本地文件,对表格进行增删改查时没有去改动本地文件,重启demo将恢复初始状态
同样的,本篇的demo源码地址放在末尾,有需要的读者可以自取
界面展示
界面结构
本次的页面结构比较简单,Rectangle作为整体背景,一个TableView展示数据内容
代码结构
Rectangle作为根元素,下属三个子元素 : EBaseTableView 、Dialog 、ETableModel
操作展示
数据展示
修改数据
删除数据
添加数据
EBaseTableView
该控件继承QtQuick.Controls 1中的TableView,对headerDelegate 、itemDelegate、rowDelegate的样式进行了自定义,具体代码如下:
EBaseTableView.qml
import QtQuick 2.13
import QtQuick.Controls 1.4
import QtQuick.Controls 2.12
import Qt.labs.settings 1.0
import EUIpackage 1.0
import EControl 1.0
TableView{
id:tableview
frameVisible:false
anchors.margins: 24
signal rowHoveredChanged(var hovered,var row)
property int currSelectedRow: -1
headerDelegate: Rectangle{
width: parent.width
height: 50
color: "white"
Rectangle{
width: 1
height: 25
color: EColor.borderColor(EColor.Border_3)
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
}
Rectangle{
width: parent.width
height: 1
anchors.bottom: parent.bottom
color: EColor.borderColor(EColor.Border_3)
}
Text {
text: styleData.value
color: EColor.textColor(EColor.Text_Main)
font.pixelSize: 16
width: parent.width
height: contentHeight
elide:Text.ElideRight
anchors.left: parent.left
anchors.leftMargin: 2
anchors.verticalCenter: parent.verticalCenter
font.family: EFont.textHanSansNormal
}
}
rowDelegate:Rectangle{
id:rowbg
width: parent.width
height: 48
color: "transparent"
border.width: 0
property int currSelectedRow: tableview.currSelectedRow
Rectangle{
width: parent.width
height: 1
color: EColor.borderColor(EColor.Border_3)
anchors.bottom: parent.bottom
}
onCurrSelectedRowChanged: {
if(currSelectedRow ===styleData.row)
rowbg.color = EColor.bgColor(EColor.BGColor_2)
else
rowbg.color = "transparent"
}
Connections{
target: tableview
onRowHoveredChanged:{
if(row ===styleData.row && currSelectedRow !==styleData.row){
if(hovered)
rowbg.color = EColor.bgColor(EColor.BGColor_2)
else
rowbg.color = "transparent"
}
}
}
}
itemDelegate: Item{
id:itemdel
width: parent.width
height: 48
clip: true
property bool hovered: false
onHoveredChanged: {
tableview.rowHoveredChanged(hovered,styleData.row)
}
MouseArea{
anchors.fill: parent
hoverEnabled: true
onEntered: itemdel.hovered= true
onExited: itemdel.hovered = false
}
Text {
text: styleData.value
color: EColor.textColor(EColor.Text_Routine)
font.pixelSize: 14
width: parent.width
height: contentHeight
elide:Text.ElideRight
anchors.verticalCenter: parent.verticalCenter
font.family: EFont.textHanSansNormal
}
}
}
Dialog
该弹窗是用于新增项目和修改项目填写信息使用的,由于只使用了一次就没控件化了,代码如下:
Dialog{
id:inputdialog
anchors.centerIn: parent
width: parent.width/2.5
height: parent.height/4*3
modal: true
closePolicy: Popup.NoAutoClose
property bool isNew: false
property int currSelectedRow: -1
background: Rectangle{
radius: 4
anchors.fill: parent
border.width: 1
border.color: EColor.borderColor(EColor.Border_1)
}
contentItem: Item{
anchors.fill: parent
Text {
id: titleText
text: inputdialog.isNew? "添加新成员" : "修改成员信息"
anchors.top: parent.top
anchors.topMargin: 12
anchors.horizontalCenter: parent.horizontalCenter
font.family: EFont.textHanSansMedium
font.pixelSize: 26
color: EColor.textColor(EColor.Text_Main)
width: contentWidth
height: contentHeight
}
Column{
id:inputColumn
width: parent.width-40
anchors.top: titleText.bottom
anchors.topMargin: 22
anchors.horizontalCenter: parent.horizontalCenter
spacing: 22
CusTipInput{
width: parent.width
height: 40
tiptext: "姓 名"
placeholderText:"请输入姓名"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "国 家"
placeholderText:"请输入国家名"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "城 市"
placeholderText:"请输入城市名"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "出生日期"
placeholderText:"请输入出生日期"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "联系方式"
placeholderText:"请输入联系方式"
}
}//end columm
EBaseBtn{
width: 100
height: 40
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: 20
cusText:"取消"
onClicked: {
inputdialog.close()
}
}
EMainBtn{
width: 100
height: 40
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 20
cusText:"确定"
onClicked: {
var childrenList = inputColumn.children
if(childrenList[0].inputText === ""){
dialogToast.showToast("姓名不能为空",EToast.StyleEnum.ERROR)
return
}
if(inputdialog.isNew){
var dataList = []
for(var i = 0;i<childrenList.length;++i){
dataList.push(childrenList[i].inputText)
}
DataHandleVM.insertNewRow(etablemodel,dataList)
}
else{
var dataObj = {}
for(i = 0;i<etablemodel.roles.length;++i){
let role = etablemodel.roles[i]
dataObj[role] = childrenList[i].inputText.toString()
}
DataHandleVM.setRowData(etablemodel,inputdialog.currSelectedRow,dataObj)
}
inputdialog.close()
inputdialog.currSelectedRow =-1
}
}
EToast{
id:dialogToast
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
}
}//end Dialog.contenItem
}//end Dialog
ETableModel
该model继承 QAbstractTableModel,然后注册到qml使用,这里写成了一个比较通用的model,复用性较高。代码如下
ECusTableModel.h
#ifndef ECUSTABLEMODEL_H
#define ECUSTABLEMODEL_H
#include <QObject>
#include <QAbstractTableModel>
class ECusTableModel : public QAbstractTableModel
{
Q_OBJECT
Q_PROPERTY(QStringList roles READ roles WRITE setRoles)
public:
ECusTableModel(QObject *parent = nullptr);
~ECusTableModel() override;
//继承基类的函数
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
//根据行和roleNmae获取数据
QVariant rowData(const int row,const QString roleName);
//根据行和roleName修改数据
bool setRowData(const int row,const QString roleName,const QVariant newValue);
//增加行到末尾
void insertNewRowData(QVariantList dataList);
//删除行
void removeRowData(int row);
QStringList roles() const;
void setRoles(const QStringList roles);
void loadData(QList<QVariantList> data);
signals:
private:
QList<QVariantList> m_data;
QStringList m_roles;
};
#endif // ECUSTABLEMODEL_H
ECusTableModel.cpp
#include "ECusTableModel.h"
#include <QDebug>
ECusTableModel::ECusTableModel(QObject *parent)
{
Q_UNUSED(parent)
}
ECusTableModel::~ECusTableModel()
{
qDebug()<<__FUNCTION__;
}
QVariant ECusTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole) {
return m_data[index.row()].at(index.column());
}
else
{
int columnIndex = role - Qt::UserRole - 1;
QModelIndex modelIndex = this->index(index.row(), columnIndex);
return m_data[modelIndex.row()].at(modelIndex.column());
}
}
int ECusTableModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return m_data.size();
}
int ECusTableModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
if(m_data.isEmpty())
return 0;
return m_data.at(0).size();
}
QHash<int, QByteArray> ECusTableModel::roleNames() const
{
QHash<int, QByteArray> roles;
for(int i = 0;i < m_roles.size();i++)
{
roles[Qt::UserRole+i+1] = m_roles.at(i).toLocal8Bit();
}
return roles;
}
bool ECusTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(index.isValid() && role > Qt::UserRole){
int row = index.row();
int column = index.column();
m_data[row][column] = value;
emit dataChanged(index,index);
return true;
}
return false;
}
QVariant ECusTableModel::rowData(const int row, const QString roleName)
{
int role = roleNames().key(roleName.toUtf8());
int column = role-Qt::UserRole-1;
if(!this->index(row,column).isValid()){
return false;
}
QVariant value = this->data(this->index(row,column),role);
return value;
}
bool ECusTableModel::setRowData(const int row, const QString roleName, const QVariant newValue)
{
int role = roleNames().key(roleName.toUtf8());
int column = role-Qt::UserRole-1;
if(!this->index(row,column).isValid()){
return false;
}
bool ret = this->setData(this->index(row,column),newValue,role);
return ret;
}
void ECusTableModel::insertNewRowData(QVariantList dataList)
{
beginInsertRows(QModelIndex(),rowCount(),rowCount());
m_data.push_back(dataList);
this->insertRow(rowCount());
endInsertRows();
}
void ECusTableModel::removeRowData(int row)
{
beginRemoveRows(QModelIndex(),row,row);
m_data.removeAt(row);
endRemoveRows();
}
QStringList ECusTableModel::roles() const
{
return m_roles;
}
void ECusTableModel::setRoles(const QStringList roles)
{
if(!roles.isEmpty()){
m_roles = roles;
}
}
void ECusTableModel::loadData(QList<QVariantList> data)
{
beginResetModel();
m_data = data;
endResetModel();
}
页面整体代码
import QtQuick 2.13
import QtQuick.Controls 1.4
import QtQuick.Controls 2.12
import Qt.labs.settings 1.0
import EUIpackage 1.0
import EControl 1.0
import "./"
import "../demoControl"
Rectangle{
color: "white"
radius: 4
EBaseTableView{
id:tableview
anchors.fill: parent
model:etablemodel
TableViewColumn {
role: "name";
title: "姓名";
width: (parent.width-50)/5
}
TableViewColumn {
role: "country";
title: "国家";
width: (parent.width-50)/5
}
TableViewColumn {
role: "city";
title: "城市";
width: (parent.width-50)/5
}
TableViewColumn {
role: "birthday";
title: "出生日期";
width: (parent.width-50)/5
}
TableViewColumn {
role: "contact";
title: "联系方式";
width: (parent.width-50)/5
}
MouseArea{
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
tableview.currSelectedRow = tableview.rowAt(mouseX,mouseY)
menu.popup(mouseX,mouseY)
}
}
CusMenu{
id:menu
Action{
text: "修改员工信息"
icon.name: "\uf044"
onTriggered: {
//打开dialog清空输入
let childrenList = inputColumn.children
let name = DataHandleVM.getRowData(etablemodel,tableview.currSelectedRow,"name")
for(var i = 0;i<etablemodel.roles.length;++i){
let value = DataHandleVM.getRowData(etablemodel,tableview.currSelectedRow,etablemodel.roles[i])
childrenList[i].inputText =value.toString()
}
inputdialog.currSelectedRow = tableview.currSelectedRow
inputdialog.isNew = false
inputdialog.open()
}
}
Action{
text: "删除当前员工"
icon.name: "\uf056"
enabled: tableview.currSelectedRow >=0
onTriggered: {
DataHandleVM.removeRow(etablemodel,tableview.currSelectedRow)
}
}
Action{
text: "添加新员工"
icon.name: "\uf055"
onTriggered: {
//打开dialog清空输入
var childrenList = inputColumn.children
for(var i = 0;i<childrenList.length;++i){
childrenList[i].inputText =""
}
inputdialog.isNew = true
inputdialog.open()
}
}
onVisibleChanged: {
if(!visible)
tableview.currSelectedRow = -1
}
}
}
Dialog{
id:inputdialog
anchors.centerIn: parent
width: parent.width/2.5
height: parent.height/4*3
modal: true
closePolicy: Popup.NoAutoClose
property bool isNew: false
property int currSelectedRow: -1
background: Rectangle{
radius: 4
anchors.fill: parent
border.width: 1
border.color: EColor.borderColor(EColor.Border_1)
}
contentItem: Item{
anchors.fill: parent
Text {
id: titleText
text: inputdialog.isNew? "添加新成员" : "修改成员信息"
anchors.top: parent.top
anchors.topMargin: 12
anchors.horizontalCenter: parent.horizontalCenter
font.family: EFont.textHanSansMedium
font.pixelSize: 26
color: EColor.textColor(EColor.Text_Main)
width: contentWidth
height: contentHeight
}
Column{
id:inputColumn
width: parent.width-40
anchors.top: titleText.bottom
anchors.topMargin: 22
anchors.horizontalCenter: parent.horizontalCenter
spacing: 22
CusTipInput{
width: parent.width
height: 40
tiptext: "姓 名"
placeholderText:"请输入姓名"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "国 家"
placeholderText:"请输入国家名"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "城 市"
placeholderText:"请输入城市名"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "出生日期"
placeholderText:"请输入出生日期"
}
CusTipInput{
width: parent.width
height: 40
tiptext: "联系方式"
placeholderText:"请输入联系方式"
}
}//end columm
EBaseBtn{
width: 100
height: 40
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: 20
cusText:"取消"
onClicked: {
inputdialog.close()
}
}
EMainBtn{
width: 100
height: 40
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 20
cusText:"确定"
onClicked: {
var childrenList = inputColumn.children
if(childrenList[0].inputText === ""){
dialogToast.showToast("姓名不能为空",EToast.StyleEnum.ERROR)
return
}
if(inputdialog.isNew){
var dataList = []
for(var i = 0;i<childrenList.length;++i){
dataList.push(childrenList[i].inputText)
}
DataHandleVM.insertNewRow(etablemodel,dataList)
}
else{
var dataObj = {}
for(i = 0;i<etablemodel.roles.length;++i){
let role = etablemodel.roles[i]
dataObj[role] = childrenList[i].inputText.toString()
}
DataHandleVM.setRowData(etablemodel,inputdialog.currSelectedRow,dataObj)
}
inputdialog.close()
inputdialog.currSelectedRow =-1
}
}
EToast{
id:dialogToast
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
}
}//end Dialog.contenItem
}//end Dialog
ETableModel{
id:etablemodel
roles:["name","country","city","birthday","contact"]
Component.onCompleted: {
DataHandleVM.loadPeopelModelData(etablemodel)
}
}
}
DataHandleVM
DataHadleVm是整个demo中处理业务逻辑的一个类,比如读取本地数据、加载数据、修改表格等。目的是为了使业务逻辑有统一的操作入口。在本次的人员管理的表格中,DataHandleVm中相关的业务处理函数如下,具体函数实现可以下载源码了解
源码地址
总结
本次分享就到这了,对你有帮助的话点个赞吧。