代理delegate,我的理解就是在table或tree中,通过代码的方式,绘制上自定义的控件,本篇文章主要就讲了如何代理、具体怎么绘制等操作(以下仅为个人心得,若有错误请指正)
代理的开始
首先,我们要建立一个tableView或者treeView,以下统称为表格table,二者相差不多。
如果对表头没有要求,我们可以在代理绘制前,先将表格的内容填好,如果有要求,可翻到文章最后一部分,表头的绘制。
以下是一个示例;
void BugReportTest::initTable() {
auto tableviewModel = new QStandardItemModel; //view类都需要model进行操作
ui.tableView->setModel(tableviewModel);
QStringList headerList; //设置表头
headerList << "1" << "2" << "3" << "4" << "5";
tableviewModel->setColumnCount(5); //列数
tableviewModel->setHorizontalHeaderLabels(headerList);
//设置列宽和行高
ui.tableView->horizontalHeader()->setMinimumHeight(53);
ui.tableView->verticalHeader()->setDefaultSectionSize(53);
//设置每个列的宽度
ui.tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
ui.tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Fixed);
ui.tableView->horizontalHeader()->setSectionResizeMode(3 ,QHeaderView::Stretch);//Stretch是随内容扩展
ui.tableView->horizontalHeader()->setSectionResizeMode(1 ,QHeaderView::Stretch);
ui.tableView->setColumnWidth(0 , 110);
ui.tableView->setColumnWidth(1 , 300);
ui.tableView->setColumnWidth(2 , 183);
ui.tableView->setColumnWidth(3 , 256);
ui.tableView->setColumnWidth(4 , 305);
//以下是插入数据
auto time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
for (int i = 0; i < 4; i++) {
tableviewModel->insertRow(i);
tableviewModel->setData(tableviewModel->index(i,0),i+1);
tableviewModel->setData(tableviewModel->index(i,1),QString("EXCEPTION_ACCESS_VIOLATION_WRITE"));
tableviewModel->setData(tableviewModel->index(i,2),QString("V%1.0").arg(i));
tableviewModel->setData(tableviewModel->index(i,3),time);
tableviewModel->setData(tableviewModel->index(i,4),time);
}
}
这样一个朴实无华的表格就建立好了,而想要代理,我们就需要创建一个类,代理类(头文件缺啥加啥):
class ProjectDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ProjectDelegate(int type , QObject *parent = nullptr);
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; //绘制单元格内容如文本
bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) override; //响应按钮事件
};
一般网上说的是可以重写五个函数,我这只重写了用的到的,其余可以看情况加,自行搜索。
创建完代理类,怎么调用,这就需要下列语句:
ui.tableView->setItemDelegate(delegate = new ProjectDelegate(1 , ui.tableView)); //添加代理
语句还可以根据第几列或者第几行去加,不过除特殊情况外,我推荐在代理类里面增加判断语句就行,这边甩个截图:
至此,我们已经完成最主要的部分了,接下里就是如何绘制。
代理的绘制
如果你有仔细看上面的内容,你肯定会知道绘制函数在哪,没错,就是我们重写的paint函数,所以我们接下来的操作就在paint函数内进行。
首先是最简单的文本绘制:
文本绘制
void ProjectDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const {
int rowIndex = index.row(); //行号
int colIndex = index.column();//列号
if (colIndex == 0 || colIndex == 4 || colIndex == 5 || colIndex == 6) { //筛选条件
painter->setPen(0x565757);//画笔颜色
QTextOption op; //文本格式在这设置
if (colIndex == 0) {
op.setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
} else {
op.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
}
QFont font; //字体属性在这
font.setPixelSize(14);
painter->setFont(font);
painter->drawText(option.rect, index.data(Qt::DisplayRole).toString(), op); //这就是绘制的函数了,中间的代表绘制表格中原本的文字
}
}
下来是qLabel,注意,因为我是节选的我的代码,筛选方式会有冲突,切勿直接cv。
QLabel绘制
QLabel label;
label.setText(index.data(Qt::DisplayRole).toString());
label.setStyleSheet("border-radius: 2px 2px 2px 2px; background: #52c41a; color: #ffffff; padding: 1px 8px; font-size: 14px;");
int labelWidth = 46;
int labelHeight = 24;
int x = option.rect.x() + (option.rect.width() - labelWidth) / 2; // 计算水平居中位置
int y = option.rect.y() + (option.rect.height() - labelHeight) / 2; // 计算垂直居中位置
label.setGeometry(x, y, labelWidth, labelHeight);
painter->save();
painter->translate(x, y); //绘制起始位置
label.render(painter); //通过调用render方法,可以将label对象的内容绘制到painter所关联的绘图设备上,从而实现在单元格中显示QLabel的文本内容
我在网上没有看到具体的QLabel绘制,可能用的比较少,因此我写的也比较片面,欢迎补充。
QPixmap绘制
const QPixmap star = QPixmap(":/res/delete.png"); //图标路径
auto width = star.width();
auto height = star.height();
auto rect = option.rect; //表格的当前区域
int x = rect.x() + rect.width() / 2 - width / 2; //起始绘制坐标,也就是左上角
int y = rect.y() + rect.height() / 2 - height / 2;
painter->drawPixmap(x, y, star);
貌似QImage绘制方式跟这个差不多?我下面就不赘述了,大家可以自己尝试。
QButton、QCheckBox等绘制
我具体没有使用,但是可以参考这位大佬的:
http://t.csdnimg.cn/TsBUa
虽然他的没有注释,但我相信如果您看了上面的内容,理解大佬的代码不是问题。
表头的绘制
话不多说,表头也需要建立一个表头类
class MyView : public QHeaderView
{
Q_OBJECT
public:
// 第一个参数设定表头方向
MyView(Qt::Orientation orientation,
QWidget * parent = nullptr) : QHeaderView(orientation, parent)
{
setSectionsClickable(true);
}
protected:
void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const override;
};
很明显,又是对函数的重写,那我们直接跳转过去:
void MyHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const {
painter->save();
QHeaderView::paintSection(painter, rect ,logicalIndex); //paintSection,是对每一段进行一个绘制,logicalIndex == 0是代表是表头的第一列
painter->restore();
if(logicalIndex == 0){
QStyleOptionHeader op;
initStyleOption(&op);
op.textAlignment = Qt::AlignCenter;
op.rect = rect;
op.text = "#";
painter->setPen(0x252525);
QFont font;
font.setPixelSize(14);
painter->setFont(font);
style()->drawControl(QStyle::CE_HeaderLabel,&op,painter);
}
}
可以看到跟tableView并无太大区别,甚至参数都十分相似,那么同理,其它方式也如tableView一般绘制。
哦对了,差点完了,在这使用表头:
auto tableviewModel = new QStandardItemModel;
ui.tableView->setModel(tableviewModel);
//表头引用
auto *headerView = new MyView(Qt::Horizontal , ui.tableView);
ui.tableView->setHorizontalHeader(headerView);
我只是个编程新手,如果有错,请务必指导我一下,求饶求饶!