计算机系统应用教程网站

网站首页 > 技术文章 正文

Qt组件库之类系统桌面图标功能完善

btikc 2024-09-08 12:02:50 技术文章 13 ℃ 0 评论

前言

上一篇文章中,我们已经实现了自定义图标Item和移动功能的实现,今天我们来继续完善该功能,包括图标Item下方的文本绘制、长文本换行和缩略、双击响应、位置自动匹配以及封装好的、简单的创建和布局方法。先来看一下最终效果:

文本绘制和长文本缩略显示功能:

图标Item位置不再随意摆放,而是成行成列,自动匹配合适的位置:

图标Item双击响应:

每种功能的实现效果还是和Windows桌面上的图标很相似的,下面我们来看看各个功能具体的实现方法。

文本绘制和长文本处理

根据我自己对Windows桌面图标的试验,总结了以下几个图标下方文本绘制的原则:

  • 文本较长时可换行。
  • 图标未选中时,文本最多绘制两行,如果文本多于两行且图标并未选中时,则只显示一行文本。
  • 图标选中时,无论文本有多长,全部显示,并且选中时的背景也随文本的函数动态变化。

知道了基本的原则,那么我们就来逐步实现每个功能,首先不考虑换行的话,文字绘制其实很简单,只需要调用QPainter中的drawText方法即可实现如下:

painter->setPen(QColor(255, 255, 255));
painter->drawText(QRectF(0, 0, 40, 40), Qt::AlignHCenter, "文本");

文本可以绘制了以后,换行如何实现?其实Qt都帮我们处理好了,我们只需要在字符串需要换行的位置插入'\n'即可,例如:

painter->drawText(QRectF(0, 0, 40, 40), Qt::AlignHCenter, "Google \nChrome");

虽然这样换行是实现了,但是如果我们想绘制一段很长的文本,总不能还要自己先数好字数,然后再挨个插入'\n',那多麻烦,使用起来太不人性化。于是我便写了个函数来实现该功能,即每隔10个字符,便插入一个换行符,文本长度限制在128个字符以内:

void IconWidget::calcTextLine()
{
    //计算文字显示的行数,默认一行显示10个字符
    m_nTextLen = strlen(m_strText.toLocal8Bit().data());
    char szText[256] = { 0 };//最多显示128个字符,但是分配256
    memcpy(szText, m_strText.toLocal8Bit().data(), (m_nTextLen > 128 ? 128 : m_nTextLen));
    m_nTextLine = 0;//记录文本行数
    for (int i=10; i<strlen(szText); i+=10)
    {
        memcpy(szText+i+1, szText+i, strlen(szText)-i);
        szText[i] = '\n';
        i++;
        m_nTextLine++;
    }
    m_strText = QString::fromLocal8Bit(szText);
}

现在文本换行功能也实现了,那么文本缩略不就更好实现了,就检测一下文本行数,如果函数大于2则只显示第一行文本,否则就全部显示。其实Windows中长文本缩略时有时会加上省略号,该功能我没实现,如果要想实现也方便,无非是替换第一行文本的最后几个字符为'.'即可。

if (m_nTextLine == 3)
{
    char szOmit[11] = { 0 };
    memcpy(szOmit, szText, 10);
    m_strText_Omit = QString::fromLocal8Bit(szOmit);
}

文本绘制最后就只剩下图标Item选中和未选中时不同呈现效果的问题了。之前我们已经在paint函数中实现了选中状态的绘制,所以选中时的文本绘制我们也放在该函数中处理。其中需要注意的就是绘制区域的问题,因为文本行数不同时,如果你每次都绘制一个固定大小的区域,虽然横向不会有问题,但是垂直方向肯定会出现要么显示不全,要么出现空白的情况,因此我们需要将绘制区域的高度与文本行数关联起来。行数在上面的calcTextLine函数中已经得到,因此绘制区域的高度就可以轻松计算出来:

m_nBottomRightY_Pro = ICON_HEIGHT + m_nTextLine * LINE_HEIGHT; //每多一行文字就多加LINE_HEIGHT的高度

注意,此时我们产生了两个绘制区域的高度,一个是未选中时的高度m_nBottomRightY,一个是选中时的高度m_nBottomRightY_Pro,这两个参数可能相等也肯能不等,完全取决于文本行数是否大于2行。但是此时我们的boundingRect()函数就需要作出修改,需要使用两者当中更高的那个高度m_nBottomRightY_Pro,否则就会因绘制区域不同,产出如下图所示的问题:

位置自动匹配

我们在Windows桌面上进行图标拖动时,无论我们在什么地方松开鼠标,图标都会自动与对应的行和列对齐,因此桌面不会显得很杂乱。之间我们自己实现的图标Item拖到哪就放置在哪,很不好看,不以规矩不成方圆,因此,我决定给其立个“规矩”。

我们知道,图标It最后停靠的位置是由鼠标释放时的位置决定的,因此我们要重写mouseReleaseEvent函数。然后在该函数中获取鼠标在Scene中的位置坐标,根据坐标和我们图标Item的摆放间隔,就可以计算出图标Item应该放置的位置,具体实现如下:

//鼠标释放响应
void IconWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    //根据鼠标在scene中的位置,计算出Item应该在的行和列
    //每行20个Item,也可以根据Scene的宽度和间隙计算出个数
    int xPos = event->scenePos().x();
    int yPos = event->scenePos().y();
    //qDebug() << xPos << ", " << yPos;
    int nRow = xPos / ICON_GAP;
    int nColumn = yPos / ICON_GAP;
    //qDebug() << "Row = " << nRow << ", Column = " << nColumn;
    this->setPos(nRow * ICON_GAP + SCENE_SHIFTING, nColumn * ICON_GAP + SCENE_SHIFTING);

    //update(); //因为上面setPos了,所以不需要手动update了
    QGraphicsItem::mouseReleaseEvent(event);
}

图标双击响应

系统桌面上的图标双击都是能打开对应的程序或文件的,而我们后面也需要双击图标Item预览对应的组件,因此我们需要实现图标Item的双击响应,当前我只是在Item被双击时弹出一个QMessageBox:

//鼠标双击响应
void IconWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
        QMessageBox::information(NULL, "J_Component", m_strText);
}

创建和自动布局功能封装

上一篇中由于时间问题,演示的时候创建自定义图标Item我用的是数组,而且创建和布局也不方便,因此趁着今天周末也对该问题进行了优化,封装了如下的方法进行图标Item的创建和在Scene中的自动布局:

//创建并添加自定义图标Item到Scene中
void Widget::addIconWidgetToScene(QString strImagePath, QString strText)
{
    IconWidget *pIconWidget = new IconWidget(strImagePath, strText);
    m_pScene->addItem(pIconWidget);

    //每行20个Item
    int nRow = m_pIconWidgetVector.count() % 20;
    int nColumn = m_pIconWidgetVector.count() / 20;
    pIconWidget->setPos(nRow * ICON_GAP + SCENE_SHIFTING, nColumn * ICON_GAP + SCENE_SHIFTING);

    m_pIconWidgetVector.append(pIconWidget);
}

这里我使用了容器存放动态创建的IconWidget对象,在窗口析构时进行内存的释放:

for (int i=0; i<m_pIconWidgetVector.count(); i++)
    {
        delete m_pIconWidgetVector[i];
        m_pIconWidgetVector[i] = NULL;
    }

因此创建图标Item就变得非常的简单:

addIconWidgetToScene(":/Images/QQ.png", "QQ");
    addIconWidgetToScene(":/Images/WeChat.png", "微信");
    addIconWidgetToScene(":/Images/QT.png", "Qt Creator");
    addIconWidgetToScene(":/Images/Emoji.png", "012345678901234567890123456789012345678901234567890123.test");
    addIconWidgetToScene(":/Images/Chrome.png", "Google Chrome");
    addIconWidgetToScene(":/Images/Computer.png", "我的电脑");
    addIconWidgetToScene(":/Images/TaoBao.png", "淘宝");
    addIconWidgetToScene(":/Images/ZhiFuBao.png", "支付宝");

    for (int i=0; i<100; i++)
        addIconWidgetToScene(":/Images/Baidu.png", "百度");

运行效果如下:

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表