在做中大型项目时,可能又成百上千个功能界面,而这些功能界面无非就是数据的输入与输出,那么对常用的控件进行定制是非常重要的。本人认为Qt的原生控件使用起来有以下几点问题:
1、 Qt控件为了兼容各种情况,设计得是比较抽象的,对于新员工来说理解起来比较困难,接口调用也复杂;
2、 不同的人对代码理解不同,Qt的控件用起来也是各显神通,后期维护起来非常困难;
3、 每个公司的产品有自己的外观风格,定制控件可以同一实现外观,无需开发人员自己处理。
可能刚刚看感觉比想象中的复杂一点,但是只要多看几遍就会清楚为什么Qt的控件要这样设计,项绘制部分参考了网上的代码。
主要自定义两个类,CustomTree和CustomTreeItemDelegate,类关系如下图:
核心代码:
void CustomTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
painter->setRenderHint(QPainter::Antialiasing);
//绘制背景
QColor colorBg;
if (option.state & QStyle::State_Selected)
{
colorBg = m_view->checkedBgColor();
}
else if (option.state & QStyle::State_MouseOver)
{
colorBg = m_view->hoverBgColor();
}
else
{
colorBg = m_view->bgColor();
}
QRect bgRect(0, option.rect.top(), m_view->width(), option.rect.height()); // 直接用option.rect,二级树左边有空白
painter->fillRect(bgRect, colorBg);
// 绘制checked状态的item的右侧三角形
if (option.state & QStyle::State_Selected)
{
painter->setPen(Qt::NoPen);
painter->setBrush(m_view->checkedTextColor());
QVector<QPointF> points;
points.append(QPointF(option.rect.right(), option.rect.top() + option.rect.height() * 0.3));
points.append(QPointF(option.rect.right(), option.rect.top() + option.rect.height() * 0.7));
points.append(QPointF(option.rect.right() - option.rect.height() * 0.3, option.rect.top() + option.rect.height() * 0.5));
painter->drawPolygon(points);
}
//绘制+-伸缩图片
if (m_model->itemFromIndex(index)->hasChildren())
{
QPixmap pix(18, 16);
pix.fill(Qt::transparent);
QPainter p(&pix);
p.setRenderHint(QPainter::Antialiasing);
int penWidth = 2;
//根据采用的背景色判断
QColor icoColorSelected;
QColor icoColorNormal;
QColor icoColorHover;
icoColorSelected = m_view->checkedTextColor();
icoColorNormal = m_view->textColor();
icoColorHover = m_view->hoverTextColor();
p.setBrush(Qt::NoBrush);
if (option.state & QStyle::State_Selected)
{
p.setPen(QPen(icoColorSelected, penWidth));
}
else if (option.state & QStyle::State_MouseOver)
{
p.setPen(QPen(icoColorHover, penWidth));
}
else
{
p.setPen(QPen(icoColorNormal, penWidth));
}
// 绘制+-线条图片
if (!m_view->isExpanded(index))
{
p.drawLine(QPointF(8, 8), QPointF(18, 8));
p.drawLine(QPointF(12, 4), QPointF(12, 12));
} else
{
p.drawLine(QPointF(8, 8), QPointF(18, 8));
}
QPixmap img(pix);
QRect targetRect = option.rect;
targetRect.setWidth(16);
targetRect.setHeight(16);
QPoint c = option.rect.center();
c.setX(8 + option.rect.x());
targetRect.moveCenter(c);
painter->drawPixmap(targetRect, img, img.rect());
}
//绘制条目文字
QColor colorText;
if (option.state & QStyle::State_Selected)
{
colorText = m_view->checkedTextColor();
}
else if (option.state & QStyle::State_MouseOver)
{
colorText = m_view->hoverTextColor();
}
else
{
colorText = m_view->textColor();
}
painter->setPen(QPen(colorText));
//绘制文字离左边的距离
int margin = 25;
QRect rect = option.rect;
rect.setWidth(rect.width() - margin);
rect.setX(rect.x() + margin);
QFont normalFont("Microsoft Yahei", 9);
painter->setFont(normalFont);
painter->drawText(rect, Qt::AlignLeft | Qt::AlignVCenter, index.data(Qt::DisplayRole).toString());
//绘制分隔符线条
if (m_view->isShowLine())
{
painter->setPen(QPen(m_view->lineColor(), 1));
if (!index.parent().isValid()) // 判断是否为一级item
{
painter->drawLine(option.rect.topLeft(), option.rect.topRight());
}
}
//绘制提示信息
QString recordInfo = m_view->infoStr(index);
//如果不需要显示提示信息或者提示信息为空则返回
if (recordInfo.isNull() || recordInfo.isEmpty() || !recordInfo.length())
{
return;
}
QPen decorationPen(option.state & QStyle::State_Selected ? m_view->checkedBgColor() : m_view->checkedTextColor());
QBrush decorationBrush(option.state & QStyle::State_Selected ? m_view->checkedTextColor() : m_view->checkedBgColor());
QFont decorationFont("Microsoft Yahei", 8);
painter->setFont(decorationFont);
//绘制提示信息背景
QRect decoration = option.rect;
decoration.setHeight(15);
decoration.moveCenter(option.rect.center());
decoration.setLeft(option.rect.right() - 55);
decoration.setRight(option.rect.right() - 15);
painter->setPen(decorationPen);
QPainterPath path;
path.addRoundedRect(decoration, 7, 7);
painter->fillPath(path, decorationBrush);
//如果是数字则将超过999的数字显示成 999+
if (recordInfo.toInt() > 999)
{
recordInfo = "999+";
}
//如果显示的提示信息长度超过4则将多余显示成省略号..
if (recordInfo.length() > 4) {
recordInfo = recordInfo.mid(0, 4) + "..";
}
painter->drawText(decoration, Qt::AlignCenter, recordInfo);
}
void CustomTree::addItem(const QString &text, QStandardItem *parent)
{
if (NULL == parent)
{
m_model->appendRow(new QStandardItem(text));
}
else
{
parent->appendRow(new QStandardItem(text));
}
}
void CustomTree::addItem(const QString &parentText, const QString &text)
{
QList<QStandardItem *> ls = m_model->findItems(parentText, Qt::MatchRecursive);
if (!ls.isEmpty())
{
foreach (QStandardItem *item, ls)
{ // 找到的都加上
addItem(text, item);
}
}
}
调用示例:
customTree->addItem("系统参数");
customTree->setItemInfo("系统参数", "管理员");
customTree->addItem("轴参数");
customTree->addItem("轴参数", "轴1");
customTree->addItem("轴参数", "轴2");
customTree->addItem("轴参数", "轴3");
customTree->addItem("用户参数");
customTree->addItem("用户参数", "公共");
customTree->addItem("用户参数", "用户1");
customTree->addItem("用户参数", "用户2");
customTree->addItem("用户参数", "用户3");
customTree->addItem("设备参数");
customTree->addItem("设备参数", "设备1");
customTree->addItem("设备参数", "设备2");
customTree->addItem("设备参数", "设备3");