修改圖形樣式
圖表
圖形的外觀由許多因素決定杆逗,所有這些因素都可以修改。以下是最重要的:
- 線型:(https://www.qcustomplot.com/doc.php/QCPGraph)->setLineStyle(..)站宗。對于所有可能的線型,請參閱QCPGraph ::LineStyle文檔或介紹頁面上的線型演示屏幕截圖益愈。[graph]
-
線條筆:QPainter 框架提供的所有筆都可用梢灭,例如實(shí)線、虛線蒸其、點(diǎn)線敏释、不同的寬度、顏色摸袁、透明度等钥顽。通過graph->setPen(..)
設(shè)置配置的筆。 - 散點(diǎn)符號:調(diào)用graph->setScatterStyle(..)以更改散點(diǎn)符號的外觀靠汁。對于所有可能的散布樣式蜂大,請參閱QCPScatterStyle文檔或介紹頁面上顯示的散布樣式演示屏幕截圖。如果您不希望在數(shù)據(jù)點(diǎn)處顯示任何散點(diǎn)符號蝶怔,請將圖形的散點(diǎn)樣式設(shè)置為 奶浦。QCPScatterStyle::ssNone
- 在圖形下或兩個圖形之間填充:QPainter 框架提供的所有畫筆都可以用于圖形填充:實(shí)心、各種圖案踢星、紋理澳叉、漸變、顏色斩狱、透明度等耳高。通過graph->setBrush(..)設(shè)置配置的畫筆扎瓶。
坐標(biāo)軸
軸的外觀可以通過改變它們所用的筆和它們的標(biāo)簽使用的字體來修改所踊。具體可以查看QCPAxis的文檔。以下是最重要屬性的快速摘要:setBasePen, setTickPen, setTickLength, setSubTickLength, setSubTickPen, setTickLabelFont, setLabelFont, setTickLabelPadding, setLabelPadding. 您可以使用 setRangeReversed反轉(zhuǎn)軸(例如概荷,使值從左到右減少而不是增加)秕岛。如果您想在軸端裝飾(例如箭頭),請使用setLowerEnding或setUpperEnding。
網(wǎng)格線
通過訪問軸的相應(yīng)QCPGrid實(shí)例來修改網(wǎng)格继薛。例如修壕,更改與左軸相連的水平網(wǎng)格線的外觀是通過訪問customPlot->yAxis->grid(). 網(wǎng)格線的外觀基本上是它們所用的筆,可以通過yAxis->grid()->setPen()設(shè)置遏考〈瑞刻度 0 處的網(wǎng)格線可以用不同的筆繪制,它可以配置為setZeroLinePen. 如果您不想用特殊的筆畫零線灌具,只需將其設(shè)置為Qt::NoPen青团,刻度 0 處的網(wǎng)格線將使用普通網(wǎng)格筆繪制。
子網(wǎng)格線默認(rèn)設(shè)置為不可見咖楣。它們可以用grid()->setSubGridVisible(true) 激活督笆。
例子
兩個圖的簡單繪圖
這是一個創(chuàng)建具有指數(shù)包絡(luò)的衰減余弦函數(shù)圖像的示例:
// add two new graphs and set their look:
customPlot->addGraph();
customPlot->graph(0)->setPen(QPen(Qt::blue)); // line color blue for first graph
customPlot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); // first graph will be filled with translucent blue
customPlot->addGraph();
customPlot->graph(1)->setPen(QPen(Qt::red)); // line color red for second graph
// generate some points of data (y0 for first, y1 for second graph):
QVector<double> x(251), y0(251), y1(251);
for (int i=0; i<251; ++i)
{
x[i] = i;
y0[i] = qExp(-i/150.0)*qCos(i/10.0); // exponentially decaying cosine
y1[i] = qExp(-i/150.0); // exponential envelope
}
// configure right and top axis to show ticks but no labels:
// (see QCPAxisRect::setupFullAxesBox for a quicker method to do this)
customPlot->xAxis2->setVisible(true);
customPlot->xAxis2->setTickLabels(false);
customPlot->yAxis2->setVisible(true);
customPlot->yAxis2->setTickLabels(false);
// make left and bottom axes always transfer their ranges to right and top axes:
connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange)));
connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange)));
// pass data points to graphs:
customPlot->graph(0)->setData(x, y0);
customPlot->graph(1)->setData(x, y1);
// let the ranges scale themselves so graph 0 fits perfectly in the visible area:
customPlot->graph(0)->rescaleAxes();
// same thing for graph 1, but only enlarge ranges (in case graph 1 is smaller than graph 0):
customPlot->graph(1)->rescaleAxes(true);
// Note: we could have also just called customPlot->rescaleAxes(); instead
// Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking:
customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
如您所見,對圖形應(yīng)用填充就像設(shè)置一個非Qt::NoBrush
畫刷. 填充將從圖表(此處為圖表 0)到平行于鍵(此處為 x)軸的零值線诱贿。如果我們想要在此圖和另一個圖之間進(jìn)行通道填充娃肿,我們將另外調(diào)用graph->setChannelFillGraph(otherGraph). 要刪除通道填充,只需0作為其他圖形傳遞珠十,填充將像以前一樣一直到達(dá)零值線料扰。要完全移除填充,請調(diào)用graph->setBrush(Qt::NoBrush)宵睦。
使用多個軸和更高級的樣式進(jìn)行繪圖
現(xiàn)在记罚,讓我們看一個更復(fù)雜的示例,用于創(chuàng)建演示屏幕截圖壳嚎,其中包含四個軸上的五個圖形桐智、紋理填充、垂直誤差線烟馅、圖例说庭、作為小數(shù)分隔符的點(diǎn)等。
customPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom)); // period as decimal separator and comma as thousand separator
customPlot->legend->setVisible(true);
QFont legendFont = font(); // start out with MainWindow's font..
legendFont.setPointSize(9); // and make a bit smaller for legend
customPlot->legend->setFont(legendFont);
customPlot->legend->setBrush(QBrush(QColor(255,255,255,230)));
// by default, the legend is in the inset layout of the main axis rect. So this is how we access it to change legend placement:
customPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignBottom|Qt::AlignRight);
// setup for graph 0: key axis left, value axis bottom
// will contain left maxwell-like function
customPlot->addGraph(customPlot->yAxis, customPlot->xAxis);
customPlot->graph(0)->setPen(QPen(QColor(255, 100, 0)));
customPlot->graph(0)->setBrush(QBrush(QPixmap("./balboa.jpg"))); // fill with texture of specified image
customPlot->graph(0)->setLineStyle(QCPGraph::lsLine);
customPlot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5));
customPlot->graph(0)->setName("Left maxwell function");
// setup for graph 1: key axis bottom, value axis left (those are the default axes)
// will contain bottom maxwell-like function with error bars
customPlot->addGraph();
customPlot->graph(1)->setPen(QPen(Qt::red));
customPlot->graph(1)->setBrush(QBrush(QPixmap("./balboa.jpg"))); // same fill as we used for graph 0
customPlot->graph(1)->setLineStyle(QCPGraph::lsStepCenter);
customPlot->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::red, Qt::white, 7));
customPlot->graph(1)->setName("Bottom maxwell function");
QCPErrorBars *errorBars = new QCPErrorBars(customPlot->xAxis, customPlot->yAxis);
errorBars->removeFromLegend();
errorBars->setDataPlottable(customPlot->graph(1));
// setup for graph 2: key axis top, value axis right
// will contain high frequency sine with low frequency beating:
customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis2);
customPlot->graph(2)->setPen(QPen(Qt::blue));
customPlot->graph(2)->setName("High frequency sine");
// setup for graph 3: same axes as graph 2
// will contain low frequency beating envelope of graph 2
customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis2);
QPen blueDotPen;
blueDotPen.setColor(QColor(30, 40, 255, 150));
blueDotPen.setStyle(Qt::DotLine);
blueDotPen.setWidthF(4);
customPlot->graph(3)->setPen(blueDotPen);
customPlot->graph(3)->setName("Sine envelope");
// setup for graph 4: key axis right, value axis top
// will contain parabolically distributed data points with some random perturbance
customPlot->addGraph(customPlot->yAxis2, customPlot->xAxis2);
customPlot->graph(4)->setPen(QColor(50, 50, 50, 255));
customPlot->graph(4)->setLineStyle(QCPGraph::lsNone);
customPlot->graph(4)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 4));
customPlot->graph(4)->setName("Some random data around\na quadratic function");
// generate data, just playing with numbers, not much to learn here:
QVector<double> x0(25), y0(25);
QVector<double> x1(15), y1(15), y1err(15);
QVector<double> x2(250), y2(250);
QVector<double> x3(250), y3(250);
QVector<double> x4(250), y4(250);
for (int i=0; i<25; ++i) // data for graph 0
{
x0[i] = 3*i/25.0;
y0[i] = qExp(-x0[i]*x0[i]*0.8)*(x0[i]*x0[i]+x0[i]);
}
for (int i=0; i<15; ++i) // data for graph 1
{
x1[i] = 3*i/15.0;;
y1[i] = qExp(-x1[i]*x1[i])*(x1[i]*x1[i])*2.6;
y1err[i] = y1[i]*0.25;
}
for (int i=0; i<250; ++i) // data for graphs 2, 3 and 4
{
x2[i] = i/250.0*3*M_PI;
x3[i] = x2[i];
x4[i] = i/250.0*100-50;
y2[i] = qSin(x2[i]*12)*qCos(x2[i])*10;
y3[i] = qCos(x3[i])*10;
y4[i] = 0.01*x4[i]*x4[i] + 1.5*(rand()/(double)RAND_MAX-0.5) + 1.5*M_PI;
}
// pass data points to graphs:
customPlot->graph(0)->setData(x0, y0);
customPlot->graph(1)->setData(x1, y1);
errorBars->setData(y1err);
customPlot->graph(2)->setData(x2, y2);
customPlot->graph(3)->setData(x3, y3);
customPlot->graph(4)->setData(x4, y4);
// activate top and right axes, which are invisible by default:
customPlot->xAxis2->setVisible(true);
customPlot->yAxis2->setVisible(true);
// set ranges appropriate to show data:
customPlot->xAxis->setRange(0, 2.7);
customPlot->yAxis->setRange(0, 2.6);
customPlot->xAxis2->setRange(0, 3.0*M_PI);
customPlot->yAxis2->setRange(-70, 35);
// set pi ticks on top axis:
customPlot->xAxis2->setTicker(QSharedPointer<QCPAxisTickerPi>(new QCPAxisTickerPi));
// add title layout element:
customPlot->plotLayout()->insertRow(0);
customPlot->plotLayout()->addElement(0, 0, new QCPTextElement(customPlot, "Way too many graphs in one plot", QFont("sans", 12, QFont::Bold)));
// set labels:
customPlot->xAxis->setLabel("Bottom axis with outward ticks");
customPlot->yAxis->setLabel("Left axis label");
customPlot->xAxis2->setLabel("Top axis label");
customPlot->yAxis2->setLabel("Right axis label");
// make ticks on bottom axis go outward:
customPlot->xAxis->setTickLength(0, 5);
customPlot->xAxis->setSubTickLength(0, 3);
// make ticks on right axis go inward and outward:
customPlot->yAxis2->setTickLength(3, 3);
customPlot->yAxis2->setSubTickLength(1, 1);
如您所見郑趁,您可以自由定義哪個軸應(yīng)該在圖表中扮演哪個角色刊驴。例如,索引為 0 的圖形使用左軸 ( yAxis) 作為其鍵寡润,底部軸 ( xAxis) 作為其值捆憎。因此,該圖相對于左軸向上站立:
為了顯示圖 1 的誤差線梭纹,我們創(chuàng)建了一個QCPErrorBars實(shí)例躲惰,它可以附加到其他繪圖表(如QCPGraph)并為它們提供誤差線。有關(guān)所用方法的進(jìn)一步說明变抽,請查看相應(yīng)的文檔础拨。
繪制日期和時間數(shù)據(jù)
接下來氮块,我們將看看如何繪制與日期和/或時間相關(guān)的數(shù)據(jù)。它基本上歸結(jié)為在各自的軸上安裝不同類型的軸QCPAxisTickerDateTime的ticker 诡宗。
// set locale to english, so we get english month names:
customPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom));
// seconds of current time, we'll use it as starting point in time for data:
double now = QDateTime::currentDateTime().toTime_t();
srand(8); // set the random seed, so we always get the same random data
// create multiple graphs:
for (int gi=0; gi<5; ++gi)
{
customPlot->addGraph();
QColor color(20+200/4.0*gi,70*(1.6-gi/4.0), 150, 150);
customPlot->graph()->setLineStyle(QCPGraph::lsLine);
customPlot->graph()->setPen(QPen(color.lighter(200)));
customPlot->graph()->setBrush(QBrush(color));
// generate random walk data:
QVector<QCPGraphData> timeData(250);
for (int i=0; i<250; ++i)
{
timeData[i].key = now + 24*3600*i;
if (i == 0)
timeData[i].value = (i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
else
timeData[i].value = qFabs(timeData[i-1].value)*(1+0.02/4.0*(4-gi)) + (i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
}
customPlot->graph()->data()->set(timeData);
}
// configure bottom axis to show date instead of number:
QSharedPointer<QCPAxisTickerDateTime> dateTicker(new QCPAxisTickerDateTime);
dateTicker->setDateTimeFormat("d. MMMM\nyyyy");
customPlot->xAxis->setTicker(dateTicker);
// configure left axis text labels:
QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);
textTicker->addTick(10, "a bit\nlow");
textTicker->addTick(50, "quite\nhigh");
customPlot->yAxis->setTicker(textTicker);
// set a more compact font size for bottom and left axis tick labels:
customPlot->xAxis->setTickLabelFont(QFont(QFont().family(), 8));
customPlot->yAxis->setTickLabelFont(QFont(QFont().family(), 8));
// set axis labels:
customPlot->xAxis->setLabel("Date");
customPlot->yAxis->setLabel("Random wobbly lines value");
// make top and right axes visible but without ticks and labels:
customPlot->xAxis2->setVisible(true);
customPlot->yAxis2->setVisible(true);
customPlot->xAxis2->setTicks(false);
customPlot->yAxis2->setTicks(false);
customPlot->xAxis2->setTickLabels(false);
customPlot->yAxis2->setTickLabels(false);
// set axis ranges to show all data:
customPlot->xAxis->setRange(now, now+24*3600*249);
customPlot->yAxis->setRange(0, 60);
// show legend with slightly transparent background brush:
customPlot->legend->setVisible(true);
customPlot->legend->setBrush(QColor(255, 255, 255, 150));
您傳遞給dateTicker->setDateTimeFormat()的字符串與傳遞給QDateTime::toString的字符串具有相同的日期格式選項(xiàng)滔蝉,請參閱 Qt 文檔。QCustomPlot 中的所有日期/時間坐標(biāo)都以自 1970 年 1 月 1 日午夜 1 日以來的秒數(shù)處理塔沃,UTC(稱為 Unix/Epoch 時間)蝠引。這也是您在調(diào)用日期/時間類QDateTime::toTime_t/或setTime_t時使用的單位。
對于亞秒級精度蛀柴,軸ticker使用浮點(diǎn)數(shù)立肘。因此,小于 1.0 的值表示相應(yīng)的秒數(shù)名扛。您可以使用QCPAxisTickerDateTime::dateTimeToKey和keyToDateTime和 QDateTime
之間進(jìn)行轉(zhuǎn)換谅年,這與 Qt 版本無關(guān)(QDateTime::toMSecsSinceEpoch
僅在 Qt 4.7 中引入)。
圖表之外:曲線肮韧、條形圖融蹂、統(tǒng)計箱線圖
到目前為止,我們只查看了圖表弄企。由于它們是如此占主導(dǎo)地位的用例超燃,QCustomPlot 為它們提供了一個專門的接口。我們一直在使用它:QCustomPlot::addGraph拘领、QCustomPlot::graph等等意乓。但這還不是全部。QCustomPlot 有一個更通用的接口约素,用于在繪圖內(nèi)繪制數(shù)據(jù)的類届良,稱為 Plottables。這個接口是圍繞抽象基類QCPAbstractPlottable構(gòu)建的圣猎。所有 Plottables 都派生自此類士葫,也是熟悉的QCPGraph類。QCustomPlot 提供了許多其他的可繪圖類:
- QCPGraph:這就是我們一直在使用的繪圖類送悔。將一系列數(shù)據(jù)點(diǎn)顯示為具有不同線型慢显、填充和散點(diǎn)的圖形。
- QCPCurve:與 QCPGraph 類似欠啤,不同之處在于它用于顯示參數(shù)曲線荚藻。與函數(shù)圖不同,它們可能有循環(huán)洁段。
- QCPBars:條形圖应狱。獲取一系列數(shù)據(jù)點(diǎn)并用條形表示它們。如果繪圖中有多個QCPBars繪圖表眉撵,它們可以相互堆疊侦香,如介紹頁面上的屏幕截圖所示。
- QCPStatisticalBox:統(tǒng)計箱線圖纽疟。采用五數(shù)匯總(最小值罐韩、下四分位數(shù)、中位數(shù)污朽、上四分位數(shù)散吵、最大值)并將其表示為統(tǒng)計框。也可以顯示異常值蟆肆。
- QCPColorMap : 一個 2D 地圖矾睦,它通過使用顏色漸變來可視化第三個數(shù)據(jù)維度。類QCPColorScale伴隨此繪圖表以可視化繪圖中的數(shù)據(jù)比例炎功。
- QCPFinancial:一個繪圖表枚冗,可用于通過使用燭臺或 OHLC 條來可視化例如股票價格的開盤價、最高價蛇损、最低價赁温、收盤價信息。
- QCPErrorBars:這是一個特殊的繪圖表淤齐,它附加到第二個繪圖表股囊,以允許在其他繪圖表的數(shù)據(jù)點(diǎn)上顯示誤差線。
與圖表不同更啄,其他繪圖表需要在new
QCustomPlot 之外創(chuàng)建稚疹。這意味著有addGraph函數(shù)的方式?jīng)]有addCurve或addBars函數(shù)。繪圖表應(yīng)屬于的 QCustomPlot 實(shí)例是從繪圖表的構(gòu)造函數(shù)中傳遞的軸推斷出來的祭务。QCustomPlot 然后取得繪圖表的所有權(quán)内狗。可以使用QCustomPlot::plottable(int index)訪問現(xiàn)有的繪圖表义锥, 并且可以使用 QCustomPlot::plottableCount檢索繪圖中的繪圖表總數(shù)(包括圖形)
這是一個創(chuàng)建包含三個條形圖的條形圖的快速示例:
QCPBars *myBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);
// now we can modify properties of myBars:
myBars->setName("Bars Series 1");
QVector<double> keyData;
QVector<double> valueData;
keyData << 1 << 2 << 3;
valueData << 2 << 4 << 8;
myBars->setData(keyData, valueData);
customPlot->rescaleAxes();
customPlot->replot();
有關(guān)其他繪圖表的更多詳細(xì)信息可以在示例項(xiàng)目和其他教程中找到其屏。此外,每種可繪圖類型在相應(yīng)類的文檔頁面上都有詳細(xì)描述缨该。
當(dāng)然偎行,絕對有可能編寫自己的繪圖表以使任何數(shù)據(jù)看起來完全符合您的需要。您應(yīng)該查看QCPAbstractPlottable文檔以獲取如何開始對其進(jìn)行子類化的指南贰拿。您還可以查看現(xiàn)有的繪圖表以了解它們是如何工作的蛤袒。為此,建議先看看 QCPBars或QCPCurve開始膨更。QCPGraph功能非常豐富妙真,因此可能不適合作為起點(diǎn)。
QCustomPlot(一):基礎(chǔ)
QCustomPlot(二):圖形繪制基礎(chǔ)