Chart Example▲
Sélectionnez
/**
**************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
***************************************************************************
*/
#include
"pieview.h"
#include <QtWidgets>
PieView::
PieView(QWidget *
parent)
:
QAbstractItemView(parent)
{
horizontalScrollBar()-&
gt;setRange(0
, 0
);
verticalScrollBar()-&
gt;setRange(0
, 0
);
}
void
PieView::
dataChanged(const
QModelIndex &
amp;topLeft,
const
QModelIndex &
amp;bottomRight,
const
QVector&
lt;int
&
gt; &
amp;roles)
{
QAbstractItemView::
dataChanged(topLeft, bottomRight, roles);
if
(!
roles.contains(Qt::
DisplayRole))
return
;
validItems =
0
;
totalValue =
0.0
;
for
(int
row =
0
; row &
lt; model()-&
gt;rowCount(rootIndex()); ++
row) {
QModelIndex index =
model()-&
gt;index(row, 1
, rootIndex());
double
value =
model()-&
gt;data(index, Qt::
DisplayRole).toDouble();
if
(value &
gt; 0.0
) {
totalValue +=
value;
validItems++
;
}
}
viewport()-&
gt;update();
}
bool
PieView::
edit(const
QModelIndex &
amp;index, EditTrigger trigger, QEvent *
event)
{
if
(index.column() ==
0
)
return
QAbstractItemView::
edit(index, trigger, event);
else
return
false
;
}
/*
Returns the item that covers the coordinate given in the view.
*/
QModelIndex PieView::
indexAt(const
QPoint &
amp;point) const
{
if
(validItems ==
0
)
return
QModelIndex();
// Transform the view coordinates into contents widget coordinates.
int
wx =
point.x() +
horizontalScrollBar()-&
gt;value();
int
wy =
point.y() +
verticalScrollBar()-&
gt;value();
if
(wx &
lt; totalSize) {
double
cx =
wx -
totalSize /
2
;
double
cy =
totalSize /
2
-
wy; // positive cy for items above the center
// Determine the distance from the center point of the pie chart.
double
d =
std::
sqrt(std::
pow(cx, 2
) +
std::
pow(cy, 2
));
if
(d ==
0
||
d &
gt; pieSize /
2
)
return
QModelIndex();
// Determine the angle of the point.
double
angle =
qRadiansToDegrees(std::
atan2(cy, cx));
if
(angle &
lt; 0
)
angle =
360
+
angle;
// Find the relevant slice of the pie.
double
startAngle =
0.0
;
for
(int
row =
0
; row &
lt; model()-&
gt;rowCount(rootIndex()); ++
row) {
QModelIndex index =
model()-&
gt;index(row, 1
, rootIndex());
double
value =
model()-&
gt;data(index).toDouble();
if
(value &
gt; 0.0
) {
double
sliceAngle =
360
*
value /
totalValue;
if
(angle &
gt;=
startAngle &
amp;&
amp; angle &
lt; (startAngle +
sliceAngle))
return
model()-&
gt;index(row, 1
, rootIndex());
startAngle +=
sliceAngle;
}
}
}
else
{
double
itemHeight =
QFontMetrics(viewOptions().font).height();
int
listItem =
int
((wy -
margin) /
itemHeight);
int
validRow =
0
;
for
(int
row =
0
; row &
lt; model()-&
gt;rowCount(rootIndex()); ++
row) {
QModelIndex index =
model()-&
gt;index(row, 1
, rootIndex());
if
(model()-&
gt;data(index).toDouble() &
gt; 0.0
) {
if
(listItem ==
validRow)
return
model()-&
gt;index(row, 0
, rootIndex());
// Update the list index that corresponds to the next valid row.
++
validRow;
}
}
}
return
QModelIndex();
}
bool
PieView::
isIndexHidden(const
QModelIndex &
amp; /*index*/
) const
{
return
false
;
}
/*
Returns the rectangle of the item at position \a index in the
model. The rectangle is in contents coordinates.
*/
QRect PieView::
itemRect(const
QModelIndex &
amp;index) const
{
if
(!
index.isValid())
return
QRect();
// Check whether the index's row is in the list of rows represented
// by slices.
QModelIndex valueIndex;
if
(index.column() !=
1
)
valueIndex =
model()-&
gt;index(index.row(), 1
, rootIndex());
else
valueIndex =
index;
if
(model()-&
gt;data(valueIndex).toDouble() &
lt;=
0.0
)
return
QRect();
int
listItem =
0
;
for
(int
row =
index.row()-
1
; row &
gt;=
0
; --
row) {
if
(model()-&
gt;data(model()-&
gt;index(row, 1
, rootIndex())).toDouble() &
gt; 0.0
)
listItem++
;
}
switch
(index.column()) {
case
0
: {
const
qreal itemHeight =
QFontMetricsF(viewOptions().font).height();
return
QRect(totalSize,
qRound(margin +
listItem *
itemHeight),
totalSize -
margin, qRound(itemHeight));
}
case
1
:
return
viewport()-&
gt;rect();
}
return
QRect();
}
QRegion PieView::
itemRegion(const
QModelIndex &
amp;index) const
{
if
(!
index.isValid())
return
QRegion();
if
(index.column() !=
1
)
return
itemRect(index);
if
(model()-&
gt;data(index).toDouble() &
lt;=
0.0
)
return
QRegion();
double
startAngle =
0.0
;
for
(int
row =
0
; row &
lt; model()-&
gt;rowCount(rootIndex()); ++
row) {
QModelIndex sliceIndex =
model()-&
gt;index(row, 1
, rootIndex());
double
value =
model()-&
gt;data(sliceIndex).toDouble();
if
(value &
gt; 0.0
) {
double
angle =
360
*
value /
totalValue;
if
(sliceIndex ==
index) {
QPainterPath slicePath;
slicePath.moveTo(totalSize /
2
, totalSize /
2
);
slicePath.arcTo(margin, margin, margin +
pieSize, margin +
pieSize,
startAngle, angle);
slicePath.closeSubpath();
return
QRegion(slicePath.toFillPolygon().toPolygon());
}
startAngle +=
angle;
}
}
return
QRegion();
}
int
PieView::
horizontalOffset() const
{
return
horizontalScrollBar()-&
gt;value();
}
void
PieView::
mousePressEvent(QMouseEvent *
event)
{
QAbstractItemView::
mousePressEvent(event);
origin =
event-&
gt;pos();
if
(!
rubberBand)
rubberBand =
new
QRubberBand(QRubberBand::
Rectangle, viewport());
rubberBand-&
gt;setGeometry(QRect(origin, QSize()));
rubberBand-&
gt;show();
}
void
PieView::
mouseMoveEvent(QMouseEvent *
event)
{
if
(rubberBand)
rubberBand-&
gt;setGeometry(QRect(origin, event-&
gt;pos()).normalized());
QAbstractItemView::
mouseMoveEvent(event);
}
void
PieView::
mouseReleaseEvent(QMouseEvent *
event)
{
QAbstractItemView::
mouseReleaseEvent(event);
if
(rubberBand)
rubberBand-&
gt;hide();
viewport()-&
gt;update();
}
QModelIndex PieView::
moveCursor(QAbstractItemView::
CursorAction cursorAction,
Qt::
KeyboardModifiers /*modifiers*/
)
{
QModelIndex current =
currentIndex();
switch
(cursorAction) {
case
MoveLeft:
case
MoveUp:
if
(current.row() &
gt; 0
)
current =
model()-&
gt;index(current.row() -
1
, current.column(),
rootIndex());
else
current =
model()-&
gt;index(0
, current.column(), rootIndex());
break
;
case
MoveRight:
case
MoveDown:
if
(current.row() &
lt; rows(current) -
1
)
current =
model()-&
gt;index(current.row() +
1
, current.column(),
rootIndex());
else
current =
model()-&
gt;index(rows(current) -
1
, current.column(),
rootIndex());
break
;
default
:
break
;
}
viewport()-&
gt;update();
return
current;
}
void
PieView::
paintEvent(QPaintEvent *
event)
{
QItemSelectionModel *
selections =
selectionModel();
QStyleOptionViewItem option =
viewOptions();
QBrush background =
option.palette.base();
QPen foreground(option.palette.color(QPalette::
WindowText));
QPainter painter(viewport());
painter.setRenderHint(QPainter::
Antialiasing);
painter.fillRect(event-&
gt;rect(), background);
painter.setPen(foreground);
// Viewport rectangles
QRect pieRect =
QRect(margin, margin, pieSize, pieSize);
if
(validItems &
lt;=
0
)
return
;
painter.save();
painter.translate(pieRect.x() -
horizontalScrollBar()-&
gt;value(),
pieRect.y() -
verticalScrollBar()-&
gt;value());
painter.drawEllipse(0
, 0
, pieSize, pieSize);
double
startAngle =
0.0
;
int
row;
for
(row =
0
; row &
lt; model()-&
gt;rowCount(rootIndex()); ++
row) {
QModelIndex index =
model()-&
gt;index(row, 1
, rootIndex());
double
value =
model()-&
gt;data(index).toDouble();
if
(value &
gt; 0.0
) {
double
angle =
360
*
value /
totalValue;
QModelIndex colorIndex =
model()-&
gt;index(row, 0
, rootIndex());
QColor color =
QColor(model()-&
gt;data(colorIndex, Qt::
DecorationRole).toString());
if
(currentIndex() ==
index)
painter.setBrush(QBrush(color, Qt::
Dense4Pattern));
else
if
(selections-&
gt;isSelected(index))
painter.setBrush(QBrush(color, Qt::
Dense3Pattern));
else
painter.setBrush(QBrush(color));
painter.drawPie(0
, 0
, pieSize, pieSize, int
(startAngle*
16
), int
(angle*
16
));
startAngle +=
angle;
}
}
painter.restore();
int
keyNumber =
0
;
for
(row =
0
; row &
lt; model()-&
gt;rowCount(rootIndex()); ++
row) {
QModelIndex index =
model()-&
gt;index(row, 1
, rootIndex());
double
value =
model()-&
gt;data(index).toDouble();
if
(value &
gt; 0.0
) {
QModelIndex labelIndex =
model()-&
gt;index(row, 0
, rootIndex());
QStyleOptionViewItem option =
viewOptions();
option.rect =
visualRect(labelIndex);
if
(selections-&
gt;isSelected(labelIndex))
option.state |=
QStyle::
State_Selected;
if
(currentIndex() ==
labelIndex)
option.state |=
QStyle::
State_HasFocus;
itemDelegate()-&
gt;paint(&
amp;painter, option, labelIndex);
++
keyNumber;
}
}
}
void
PieView::
resizeEvent(QResizeEvent *
/* event */
)
{
updateGeometries();
}
int
PieView::
rows(const
QModelIndex &
amp;index) const
{
return
model()-&
gt;rowCount(model()-&
gt;parent(index));
}
void
PieView::
rowsInserted(const
QModelIndex &
amp;parent, int
start, int
end)
{
for
(int
row =
start; row &
lt;=
end; ++
row) {
QModelIndex index =
model()-&
gt;index(row, 1
, rootIndex());
double
value =
model()-&
gt;data(index).toDouble();
if
(value &
gt; 0.0
) {
totalValue +=
value;
++
validItems;
}
}
QAbstractItemView::
rowsInserted(parent, start, end);
}
void
PieView::
rowsAboutToBeRemoved(const
QModelIndex &
amp;parent, int
start, int
end)
{
for
(int
row =
start; row &
lt;=
end; ++
row) {
QModelIndex index =
model()-&
gt;index(row, 1
, rootIndex());
double
value =
model()-&
gt;data(index).toDouble();
if
(value &
gt; 0.0
) {
totalValue -=
value;
--
validItems;
}
}
QAbstractItemView::
rowsAboutToBeRemoved(parent, start, end);
}
void
PieView::
scrollContentsBy(int
dx, int
dy)
{
viewport()-&
gt;scroll(dx, dy);
}
void
PieView::
scrollTo(const
QModelIndex &
amp;index, ScrollHint)
{
QRect area =
viewport()-&
gt;rect();
QRect rect =
visualRect(index);
if
(rect.left() &
lt; area.left()) {
horizontalScrollBar()-&
gt;setValue(
horizontalScrollBar()-&
gt;value() +
rect.left() -
area.left());
}
else
if
(rect.right() &
gt; area.right()) {
horizontalScrollBar()-&
gt;setValue(
horizontalScrollBar()-&
gt;value() +
qMin(
rect.right() -
area.right(), rect.left() -
area.left()));
}
if
(rect.top() &
lt; area.top()) {
verticalScrollBar()-&
gt;setValue(
verticalScrollBar()-&
gt;value() +
rect.top() -
area.top());
}
else
if
(rect.bottom() &
gt; area.bottom()) {
verticalScrollBar()-&
gt;setValue(
verticalScrollBar()-&
gt;value() +
qMin(
rect.bottom() -
area.bottom(), rect.top() -
area.top()));
}
update();
}
/*
Find the indices corresponding to the extent of the selection.
*/
void
PieView::
setSelection(const
QRect &
amp;rect, QItemSelectionModel::
SelectionFlags command)
{
// Use content widget coordinates because we will use the itemRegion()
// function to check for intersections.
QRect contentsRect =
rect.translated(
horizontalScrollBar()-&
gt;value(),
verticalScrollBar()-&
gt;value()).normalized();
int
rows =
model()-&
gt;rowCount(rootIndex());
int
columns =
model()-&
gt;columnCount(rootIndex());
QModelIndexList indexes;
for
(int
row =
0
; row &
lt; rows; ++
row) {
for
(int
column =
0
; column &
lt; columns; ++
column) {
QModelIndex index =
model()-&
gt;index(row, column, rootIndex());
QRegion region =
itemRegion(index);
if
(region.intersects(contentsRect))
indexes.append(index);
}
}
if
(indexes.size() &
gt; 0
) {
int
firstRow =
indexes.at(0
).row();
int
lastRow =
firstRow;
int
firstColumn =
indexes.at(0
).column();
int
lastColumn =
firstColumn;
for
(int
i =
1
; i &
lt; indexes.size(); ++
i) {
firstRow =
qMin(firstRow, indexes.at(i).row());
lastRow =
qMax(lastRow, indexes.at(i).row());
firstColumn =
qMin(firstColumn, indexes.at(i).column());
lastColumn =
qMax(lastColumn, indexes.at(i).column());
}
QItemSelection selection(
model()-&
gt;index(firstRow, firstColumn, rootIndex()),
model()-&
gt;index(lastRow, lastColumn, rootIndex()));
selectionModel()-&
gt;select(selection, command);
}
else
{
QModelIndex noIndex;
QItemSelection selection(noIndex, noIndex);
selectionModel()-&
gt;select(selection, command);
}
update();
}
void
PieView::
updateGeometries()
{
horizontalScrollBar()-&
gt;setPageStep(viewport()-&
gt;width());
horizontalScrollBar()-&
gt;setRange(0
, qMax(0
, 2
*
totalSize -
viewport()-&
gt;width()));
verticalScrollBar()-&
gt;setPageStep(viewport()-&
gt;height());
verticalScrollBar()-&
gt;setRange(0
, qMax(0
, totalSize -
viewport()-&
gt;height()));
}
int
PieView::
verticalOffset() const
{
return
verticalScrollBar()-&
gt;value();
}
/*
Returns the position of the item in viewport coordinates.
*/
QRect PieView::
visualRect(const
QModelIndex &
amp;index) const
{
QRect rect =
itemRect(index);
if
(!
rect.isValid())
return
rect;
return
QRect(rect.left() -
horizontalScrollBar()-&
gt;value(),
rect.top() -
verticalScrollBar()-&
gt;value(),
rect.width(), rect.height());
}
/*
Returns a region corresponding to the selection in viewport coordinates.
*/
QRegion PieView::
visualRegionForSelection(const
QItemSelection &
amp;selection) const
{
int
ranges =
selection.count();
if
(ranges ==
0
)
return
QRect();
QRegion region;
for
(int
i =
0
; i &
lt; ranges; ++
i) {
const
QItemSelectionRange &
amp;range =
selection.at(i);
for
(int
row =
range.top(); row &
lt;=
range.bottom(); ++
row) {
for
(int
col =
range.left(); col &
lt;=
range.right(); ++
col) {
QModelIndex index =
model()-&
gt;index(row, col, rootIndex());
region +=
visualRect(index);
}
}
}
return
region;
}