效果图:
首先这个缩放是鼠标放置在曲线的一个点然后开始在这个点展开与收缩,(因为懒所以没有增加横纵坐标的显示,在代码中设置都是0-100)。
绘制曲线是在C++中绘制的,自己写了一个继承QQuickPaintedItem的CurveItem,图上的数据都是随机生成。
curveitem.h 见下面代码:
#ifndef CURVEITEM_H
#define CURVEITEM_H
#include <QQuickPaintedItem>
#include <QPainter>
#include <QObject>
class CurveItem : public QQuickPaintedItem
{
Q_OBJECT
// Q_PROPERTY( QString style READ style WRITE setStyle )
Q_PROPERTY( float itemWidth WRITE setItemWidth )
Q_PROPERTY( float xAxisMax READ xAxisMax WRITE setxAxisMax )
Q_PROPERTY( float xAxisMin READ xAxisMin WRITE setXAxisMin )
Q_PROPERTY( float yAxisMax READ yAxisMax WRITE setYAxisMax )
Q_PROPERTY( float yAxisMin READ yAxisMin WRITE setYAxisMin )
Q_PROPERTY( float xAxisWidth READ xAxisWidth WRITE setXAxisWidth NOTIFY xAxisWidthChanged )
Q_PROPERTY( float yAxisHeight READ yAxisHeight WRITE setYAxisHeight )
public:
explicit CurveItem(QQuickItem *parent = 0);
virtual ~CurveItem();
void paint(QPainter *painter);
float xAxisMax() { return mf_x_axis_max; }
float xAxisMin() { return mf_x_axis_min; }
float yAxisMax() { return mf_y_axis_max; }
float yAxisMin() { return mf_y_axis_min; }
float xAxisWidth() { return mf_x_axis_width; }
float yAxisHeight() { return mf_y_axis_height; }
signals:
void xAxisWidthChanged( float width );
public slots:
void setItemWidth( float f_item_width ) { mf_item_width = f_item_width; }
void setxAxisMax( float f_x_axis_max ) { mf_x_axis_max = f_x_axis_max; }
void setXAxisMin( float f_x_axis_min ) { mf_x_axis_min = f_x_axis_min; }
void setYAxisMax( float f_y_axis_max ) { mf_y_axis_max = f_y_axis_max; }
void setYAxisMin( float f_y_axis_min ) { mf_y_axis_min = f_y_axis_min; }
void setXAxisWidth( float f_x_axis_width ) { mf_x_axis_width = f_x_axis_width; emit xAxisWidthChanged( f_x_axis_width ); }
void setYAxisHeight( float f_y_axis_height ) { mf_y_axis_height = f_y_axis_height; }
void add_line_data( QList<QString> s_temper_list, QString s_color );
void remove_line_data( int index ) { m_line_temper_assemble.removeAt( index ); }
void clear_data() { m_line_temper_assemble.clear(); }
void rePaint() { this->update(); }
float xAxis_value_to_pos( float f_value );
float yAxis_value_to_pos( float f_value );
int xAxis_pos_to_point( float f_pos );
//private:
// void calculate_xAxis_scale();
// void calculate_yAxis_scale();
private:
typedef struct _LineAna{
QString lineColor;
QList<QString> temperAssemble;
} LineAna;
float mf_item_width;
float mf_x_axis_max;
float mf_x_axis_min;
float mf_y_axis_max;
float mf_y_axis_min;
float mf_x_axis_width;
float mf_y_axis_height;
QList<LineAna*> m_line_temper_assemble;
};
#endif // CURVEITEM_H
curveitem.cpp :
#include "curveitem.h"
#include <iostream>
#include <time.h>
#include "stdlib.h"
#include <QPen>
#include <QDebug>
CurveItem::CurveItem(QQuickItem *parent):
QQuickPaintedItem(parent)
{
mf_x_axis_min = 0;
mf_x_axis_max = 100;
mf_y_axis_min = 0;
mf_y_axis_max = 100;
LineAna *initAna = new LineAna;
double x;
srand((unsigned)time(NULL)); //srand(3)
for( int i = 0; i < 100; ++i ) {
x = rand();
initAna->temperAssemble.append( QString::number( x / 1000 + 30, 'f', 0 ) );
}
initAna->lineColor = "black";
m_line_temper_assemble.append( initAna );
qDebug() << "temperAssemble" << initAna->temperAssemble;
}
CurveItem::~CurveItem()
{
}
void CurveItem::paint(QPainter *painter)
{
if( m_line_temper_assemble.size() <= 0 ) {
qDebug() << "Empty";
return;
}
QPen pen;
pen.setWidth( 1 );
QFont font = painter->font();
font.setPixelSize( 12 );
painter->setFont( font );
{
for( int i = 0; i < m_line_temper_assemble.size(); i++ ) {
QList<QString> s_one_line_data = m_line_temper_assemble.at(i)->temperAssemble;
if( s_one_line_data.length() < 1 ) {
qDebug() << "CurveItem::paint() s_one_line_data.length() < 1";
break;
}
pen.setColor( QColor( m_line_temper_assemble.at(i)->lineColor ) );
painter->setPen( pen );
QPainterPath line_path;
for( int j = 0; j < s_one_line_data.length(); j++ ) {
float f_point = s_one_line_data.at(j).toFloat();
QPointF point( xAxis_value_to_pos( j ), yAxis_value_to_pos( f_point ) );
if( j == 0 ) {
line_path.moveTo( point );
} else {
line_path.lineTo( point );
}
}
painter->drawPath( line_path );
}
}
}
float CurveItem::xAxis_value_to_pos(float f_value)
{
float f_pos;
float f_min_axis, f_range_axis;
f_min_axis = mf_x_axis_min;
f_range_axis = mf_x_axis_max - mf_x_axis_min;
f_pos = ( f_value - f_min_axis ) * ( mf_item_width / f_range_axis );
return f_pos;
}
float CurveItem::yAxis_value_to_pos(float f_value)
{
float f_pos;
f_pos = ( mf_y_axis_max - f_value ) * ( mf_y_axis_height / ( mf_y_axis_max - mf_y_axis_min ) );
return f_pos;
}
int CurveItem::xAxis_pos_to_point(float f_pos)
{
float f_range_axis = mf_x_axis_max - mf_x_axis_min;
int n_point = f_pos / ( mf_item_width / f_range_axis ) + mf_x_axis_min;
return n_point;
}
void CurveItem::add_line_data(QList<QString> s_temper_list, QString s_color)
{
LineAna *new_ana = new LineAna;
new_ana->lineColor = s_color;
new_ana->temperAssemble = s_temper_list;
m_line_temper_assemble.append( new_ana );
}
然后在main.cpp注册一个QML类,最后是main.qml:
import QtQuick 2.3
import QtQuick.Window 2.2
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import CurveItem 1.0
Window {
id: main_window
visible: true
width: 800
height: 400
function xAxis_pos_to_point( pos ) {
return curve.xAxis_pos_to_point( pos )
}
function xAxis_point_to_pos( point ) {
return curve.xAxis_value_to_pos( point )
}
CurveItem {
id: curve
width: 500
height: 400
xAxisWidth: 500
yAxisHeight: 400
itemWidth: width
onXChanged: {
console.log( "CurveItem x=",x )
}
onWidthChanged: {
console.log( "CurveItem width=", width )
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
drag.axis: Drag.XAxis
drag.minimumX: parent.xAxisWidth - parent.width
drag.maximumX: 0
onWheel: {
var hovered_point, change_pos, moved_pos
var init_item_width = curve.width
var init_item_x = curve.x
if( wheel.angleDelta.y > 0 ) {
init_item_width += 500
if( init_item_width <= curve.xAxisWidth * 10 ) {
hovered_point = xAxis_pos_to_point( wheel.x )
curve.width = init_item_width
change_pos = xAxis_point_to_pos( hovered_point )
moved_pos = change_pos - wheel.x
curve.x -= moved_pos
console.log("onWheel moved_pos=", moved_pos, "curve.x=", curve.x , "hovered_point=",hovered_point, "change_pos=",change_pos )
}
} else {
init_item_width -= 500
if( init_item_width >= curve.xAxisWidth ) {
hovered_point = xAxis_pos_to_point( wheel.x )
curve.width = init_item_width
change_pos = xAxis_point_to_pos( hovered_point )
moved_pos = change_pos - wheel.x
if( curve.x < 0 && curve.x + curve.width > curve.xAxisWidth ) {
curve.x -= moved_pos
} else if( curve.x + curve.width <= curve.xAxisWidth ) {
curve.x = curve.xAxisWidth - curve.width
}
} else if( init_item_width < 0 ) {
hovered_point = xAxis_pos_to_point( wheel.x )
curve.width = curve.xAxisWidth
change_pos = xAxis_point_to_pos( hovered_point )
moved_pos = change_pos - wheel.x
if( curve.x < 0 && curve.x + curve.width > curve.xAxisWidth ) {
curve.x -= moved_pos
} else if( curve.x + curve.width <= curve.xAxisWidth ) {
curve.x = curve.xAxisWidth - curve.width
}
}
}
if( curve.x > 0 ) {
curve.x = 0
}
curve.rePaint()
}
}
}
SpreadView {
id: view_rect
anchors.right: parent.right
anchors.top: parent.top
anchors.bottom: parent.bottom
width: 300
color: "#ebebeb"
clip: true
}
Component.onCompleted: {
curve.rePaint()
}
}
这里的SpreadView是前两篇文章中实现的,在这里写在了一个新的qml文件内。
实现局部放大的功能思路:
1.记录下鼠标悬停的横坐标值,放大整个Item的width(CurveItem绘制时是按坐标值的比例绘制的,所以放大后,整个曲线也会展开)
2.放大width后原本停留在n位置的point会往右移动,所以要让这个point回到原来的n位置:计算出偏差值,然后再将这个item减去该值,此时这item的x坐标会是负值。但整个window的大小固定,所以超出部分会看不到。