QCustomPlot之热力图(十四)

本章节是仿造echarts的热力图:地址

效果图

QCustomPlot中热力图叫QCPColorMap,所以应该称为颜色图更为合适一点,一般来说一副颜色图会由两个部分组成

  • QCPColorMap 颜色图
  • QCPColorScale 色条,供颜色图取色用的
    除了这两个,还有一个重要的家伙,那就是颜色渐变QCPColorGradient,它决定了颜色图的数据对应的是什么颜色,QCPColorGradient预定义了9种颜色渐变,如下图所示


    预定义的颜色渐变

颜色图除了x、y两个位置,还有一个z位置,z位置对应着颜色渐变中的颜色取值位置

静态热力图

void MainWindow::setupHeatmapDemo(QCustomPlot *customPlot)
{
    QVector<QString> hours = {"12a", "1a", "2a", "3a", "4a", "5a", "6a",
            "7a", "8a", "9a","10a","11a",
            "12p", "1p", "2p", "3p", "4p", "5p",
            "6p", "7p", "8p", "9p", "10p", "11p"};
    QVector<QString> days = {"Saturday", "Friday", "Thursday",
            "Wednesday", "Tuesday", "Monday", "Sunday"};

    QVector<QVector<double>> data = {{0,0,5},{0,1,1},{0,2,0},{0,3,0},{0,4,0},{0,5,0},{0,6,0},{0,7,0},{0,8,0},{0,9,0},{0,10,0},{0,11,2},{0,12,4},{0,13,1},{0,14,1},{0,15,3},{0,16,4},{0,17,6},{0,18,4},
                            {0,19,4},{0,20,3},{0,21,3},{0,22,2},{0,23,5},{1,0,7},{1,1,0},{1,2,0},{1,3,0},{1,4,0},{1,5,0},{1,6,0},{1,7,0},{1,8,0},{1,9,0},{1,10,5},{1,11,2},{1,12,2},{1,13,6},{1,14,9},{1,15,11},
                            {1,16,6},{1,17,7},{1,18,8},{1,19,12},{1,20,5},{1,21,5},{1,22,7},{1,23,2},{2,0,1},{2,1,1},{2,2,0},{2,3,0},{2,4,0},{2,5,0},{2,6,0},{2,7,0},{2,8,0},{2,9,0},{2,10,3},{2,11,2},{2,12,1},
                            {2,13,9},{2,14,8},{2,15,10},{2,16,6},{2,17,5},{2,18,5},{2,19,5},{2,20,7},{2,21,4},{2,22,2},{2,23,4},{3,0,7},{3,1,3},{3,2,0},{3,3,0},{3,4,0},{3,5,0},{3,6,0},{3,7,0},{3,8,1},
                            {3,9,0},{3,10,5},{3,11,4},{3,12,7},{3,13,14},{3,14,13},{3,15,12},{3,16,9},{3,17,5},{3,18,5},{3,19,10},{3,20,6},{3,21,4},{3,22,4},{3,23,1},{4,0,1},{4,1,3},{4,2,0},{4,3,0},{4,4,0},
                            {4,5,1},{4,6,0},{4,7,0},{4,8,0},{4,9,2},{4,10,4},{4,11,4},{4,12,2},{4,13,4},{4,14,4},{4,15,14},{4,16,12},{4,17,1},{4,18,8},{4,19,5},{4,20,3},{4,21,7},{4,22,3},{4,23,0},{5,0,2},
                            {5,1,1},{5,2,0},{5,3,3},{5,4,0},{5,5,0},{5,6,0},{5,7,0},{5,8,2},{5,9,0},{5,10,4},{5,11,1},{5,12,5},{5,13,10},{5,14,5},{5,15,7},{5,16,11},{5,17,6},{5,18,0},{5,19,5},{5,20,3},{5,21,4},
                            {5,22,2},{5,23,0},{6,0,1},{6,1,0},{6,2,0},{6,3,0},{6,4,0},{6,5,0},{6,6,0},{6,7,0},{6,8,0},{6,9,0},{6,10,1},{6,11,0},{6,12,2},{6,13,1},{6,14,3},{6,15,4},{6,16,0},{6,17,0},{6,18,0},
                            {6,19,0},{6,20,1},{6,21,2},{6,22,2},{6,23,6}};

    QCPColorMap *heatmap = new QCPColorMap(customPlot->xAxis, customPlot->yAxis);  // 构造一个颜色图
    heatmap->data()->setSize(hours.size(), days.size());   // 设置颜色图数据维度,其内部维护着一个一维数组(一般表现为二维数组),这里可以理解为有多少个小方块
    heatmap->data()->setRange(QCPRange(0.5, hours.size() - 0.5), QCPRange(0.5, days.size() - 0.5));  // 颜色图在x、y轴上的范围

    // 设置轴的显示,这里使用文字轴,如果这部分还不会的请看 QCustomPlot之个性化外观(二)这章节
    QSharedPointer<QCPAxisTickerText> xTicker(new QCPAxisTickerText);
    QSharedPointer<QCPAxisTickerText> yTicker(new QCPAxisTickerText);
    xTicker->setTicks(labelPositions(hours, 0.5), hours);
    yTicker->setTicks(labelPositions(days, 0.5), days);
    xTicker->setSubTickCount(1);
    yTicker->setSubTickCount(1);
    customPlot->xAxis->setTicker(xTicker);
    customPlot->yAxis->setTicker(yTicker);
    customPlot->xAxis->grid()->setPen(Qt::NoPen);
    customPlot->yAxis->grid()->setPen(Qt::NoPen);
    customPlot->xAxis->grid()->setSubGridVisible(true);
    customPlot->yAxis->grid()->setSubGridVisible(true);
    customPlot->xAxis->setSubTicks(true);
    customPlot->yAxis->setSubTicks(true);
    customPlot->xAxis->setTickLength(0);
    customPlot->yAxis->setTickLength(0);
    customPlot->xAxis->setSubTickLength(6);
    customPlot->yAxis->setSubTickLength(6);
    customPlot->xAxis->setRange(0, hours.size());
    customPlot->yAxis->setRange(0, days.size());

    for (int x = 0; x < hours.size(); ++x) {
        for (int y = 0; y < days.size(); ++y) {
            int z = data.at(hours.size() * y + x).at(2);
            if (z) heatmap->data()->setCell(x, y, z);     // 如果z不为0,则设置颜色值的位置
            else heatmap->data()->setAlpha(x, y, 0);  // z为0,设置为透明
        }
    }

    QCPColorScale *colorScale = new QCPColorScale(customPlot);  // 构造一个色条
    colorScale->setType(QCPAxis::atBottom);   // 水平显示
    customPlot->plotLayout()->addElement(1, 0, colorScale); // 在颜色图下面显示
    heatmap->setColorScale(colorScale); 
    QCPColorGradient gradient;  // 色条使用的颜色渐变
    gradient.setColorStopAt(0.0, QColor("#f6efa6"));   // 设置色条开始时的颜色
    gradient.setColorStopAt(1.0, QColor("#bf444c"));  // 设置色条结束时的颜色
    heatmap->setGradient(gradient);
//    colorMap->rescaleDataRange();        // 自动计算数据范围,数据范围决定了哪些数据值映射到QCPColorGradient的颜色渐变当中
    heatmap->setDataRange(QCPRange(0, 10));     // 为了保持与echart的例子一致,我们这里手动设置数据范围
    heatmap->setInterpolate(false);         // 为了显示小方块,我们禁用插值

    // 保持色条与轴矩形边距一致
    QCPMarginGroup *marginGroup = new QCPMarginGroup(customPlot);
    customPlot->axisRect()->setMarginGroup(QCP::msLeft | QCP::msRight, marginGroup);
    colorScale->setMarginGroup(QCP::msLeft | QCP::msRight, marginGroup);
}

其中labelPositions是返回labels所对应的位置的

QVector<double> labelPositions(const QVector<QString> &labels, double offset = 0)
{
    QVector<double> result(labels.size());
    for (int i = 0; i < labels.size(); ++i)
        result[i] = i + offset;
    return result;
}

动态热力图

动态热力图其实就是重新设置z的值

void MainWindow::setupDynamicHeatmapDemo(QCustomPlot *customPlot)
{
    setupHeatmapDemo(customPlot);

    connect(&dataTimer, SIGNAL(timeout()), this, SLOT(dynamicHeatmapSlot()));
    dataTimer.start(1000);
}
void MainWindow::dynamicHeatmapSlot()
{
    auto *colorMap = static_cast<QCPColorMap *>(ui->customPlot->plottable(0));

    int keySize = colorMap->data()->keySize();
    int valueSize = colorMap->data()->valueSize();
    for (int x = 0; x < keySize; ++x) {
        for (int y = 0; y < valueSize; ++y) {
            if (colorMap->data()->alpha(x, y))  // 当前不是透明的,就重新设置z的值
                colorMap->data()->setCell(x, y, QRandomGenerator::global()->bounded(1, 15));
        }
    }
    ui->customPlot->replot();
}
动态热力图
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容