QT传输函数控件设计9 初步设计视口类
我们先按照之前的布局设计中,先把 TsfunDockWidget 以及 TsfunWidget 对象设计完。注意 DockWidget 里的中心窗体设置。然后定义继承自QWidget的类TsfunWidget类以及继承自QGraphicsView类的TsfunView类,并把这个类实例化加到TsfunWidget类里面,为了玩玩布局我们也可以在TsfunWidget的布局里再加个按键(没啥用,就是添加个东西而已)。
然后现在先写一下我们的类的基本框架
#ifndef __TransferfunctionView_H__
#define __TransferfunctionView_H__
#include <QGraphicsView>
#include <QMouseEvent>
#include "margin.hpp"
class TsfunView : public QGraphicsView{
Q_OBJECT
public:
TsfunView(QGraphicsView * parent = Q_NULLPTR);
~TsfunView();
Margin viewMargin;
private:
void mousePressEvent(QMouseEvent * event);
void mouseReleaseEvent(QMouseEvent * pEvent);
void resizeEvent(QResizeEvent* MyResizeEvent);
};
#endif
目前只是在里面定义了一个Margin区域而已。然后我们进行初始化:
TsfunView::TsfunView(QGraphicsView * parent) : QGraphicsView(parent) ,
viewMargin(0, 0, 0, 150),
selectedItemFlags(0),
moveForFlags(0)
{
setMinimumHeight(150);
setMinimumWidth(300);
scene = new QGraphicsScene;
scene->setBackgroundBrush(QColor(225,255,255,255));
setScene(scene);
setRenderHint(QPainter::Antialiasing);
//任何时候都不使用滚轮
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
}
TsfunView::~TsfunView() {
}
我们设置了区域的边界,注意这里的第四个参数150,表示前面所说的bottom+remained区域的大小,然后设置了View的最小大小,然后在里面定义一个场景,并设置显示为 Antialiasing ,抗锯齿效果。然后设置任何时候都不用滚轮。其他函数目前还没有要写的东西。先把函数简单创建完就行了。
接下来我们考虑:要在场景里放些什么东西呢?
1. 首先我们要定义一个矩形图形项类,用它来画坐标轴。
2. 然后我们再定义一个矩形图形项类,用它来画小圆点。每次update都要把所有点都画一次。
首先我们要放置的东西也就这俩。那么我们现在就根据Margin来定义一下画坐标轴的矩形类:
#pragma once
#ifndef __TsfFunGrid_H__
#define __TsfFunGrid_H__
#include <QGraphicsRectItem>
#include "margin.hpp"
class TsfunGridItem : public QGraphicsRectItem {
public:
TsfunGridItem(QGraphicsRectItem * parent = Q_NULLPTR);
~TsfunGridItem();
void setGridHeight(int height) {
gridHeight = height;
}
void setGridWidth(int width) {
gridWidth = width;
}
void setDataWidth(int width) {
dataWidth = width;
}
Margin gridMargin;
void calculate();
int returnTopBoundary() {
return lineHeightStart;
}
int returnLeftBoundary() {
return lineWidthStart;
}
int returnRightBoundary() {
return lineWidthEnd;
}
int returnBottomBoundary() {
return lineHeightEnd;
}
int lineWidthStart;
int lineWidthEnd;
int lineHeightStart;
int lineHeightEnd;
private:
int gridHeight;
int gridWidth;
int dataWidth;
int segment;
int gridXNums;//x方向上的段数
int gridYNums;//y方向上的段数
int segmentHeight;//段高 像素数
int segmentWidth;//段宽 像素数
int remainLength;//剩余的长度(像素)
void paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget = 0);
};
#endif
我们来一 一分析一下里面所有的变量的作用:
首先是 Margin gridMargin; 表示我们期望的中间的绿框的区域距离各个边界的距离。注意一点:这只是我们的期望,因为我们的坐标值是离散的,而且数据的范围(比如-1000——3000)都在变,总不可能我们用横排的4000个值来表示这4000个坐标吧?所以我们只能先根据期望的绿框区域计算出距离显示数据范围最近的值,然后重新分配坐标。
void TsfunGridItem::calculate() {
//注意这里的Margin是给画线区设置的。
//数据400个为一段
gridXNums = dataWidth / segment;
int gridXRemain = dataWidth % segment;
double remainDot = (double)gridXRemain / (double)segment;
double segmentDot = gridXNums + remainDot;
lineWidthStart = gridMargin.leftToBoundary();//-2
int lineWidthLength = gridWidth - gridMargin.leftToBoundary() - gridMargin.rightToBoundary();
segmentWidth = lineWidthLength / segmentDot;
remainLength = segmentWidth*remainDot;
lineWidthEnd = gridMargin.leftToBoundary() + gridXNums*segmentWidth + remainLength;
gridYNums = 10;
int lineHeightLength = gridHeight - gridMargin.topToBoundary() - gridMargin.bottomToBoundary();
segmentHeight = lineHeightLength / gridYNums;
lineHeightStart = gridMargin.topToBoundary();
lineHeightEnd = gridMargin.topToBoundary() + segmentHeight*gridYNums;
}
所以我们定义了dataWidth,表示数据宽度,假设这里是4024。然后我们定义了segment表示每段数据量(比如400个数据表示在一个格子里。)然后我们定义了gridXNums表示x方向上的段数,即4024/400的值,但是因为没有除尽,所以还有一个remainDot,表示的是小数上剩余的长度占每一段长度的比例。以及segmentDot用小数表示一共多少段,画线中,从左到右的起始点我们设置为lineWidthStart,线长lineWidthLength(理论长度)是用格子长度除以小数表示的段数,然后从lineWidthStart进行累加,得到终点。
高度求法也一样。通过求值,我们最后得出lineWidthStart画线横轴起点,lineWidthEnd画线横轴终点,lineHeightStart画线纵轴起点,注意纵轴因为直接是用百分之0显示到百分之100,所以不需要dataWidth表示数据宽度,gridYNums直接设置为10就好了。
然后我们在
void TsfunGridItem::paint(QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget)
{
painter->setPen(QPen(QColor(0,25,25,100)));
int i = 0;
for (i = 0;i<=gridYNums;i++) {
painter->drawLine(lineWidthStart-2, lineHeightStart+i*segmentHeight,
lineWidthEnd, lineHeightStart + i*segmentHeight);
}
for (i = 0;i<gridXNums;i++) {
painter->drawLine(lineWidthStart + i*segmentWidth, lineHeightStart,
lineWidthStart + i*segmentWidth, lineHeightEnd);
}painter->drawLine(lineWidthStart + i*segmentWidth + remainLength, lineHeightStart,
lineWidthStart + i*segmentWidth + remainLength, lineHeightEnd);
//关于点和坐标对应关系: 点只记录实际坐标(单位化的)。 坐标轴上每个点都映射到坐标里。
//DebugText::getDebugText()->addContents("lalala1");
}
这个函数里根据我们生成的值来画点,为了左边的横线多处一点来,我们在这里设置了lineWidthStart-2作为起始点。
之后我们把该图形项添加到View构造函数里面,添加到场景中去,然后在resizeEvent函数里添加:
int sceneForGridWidth;
setSceneRect(0, 0, width(), height() - 2);
//scene->clear();
dataGrid->setGridHeight(height()-2 - viewMargin.bottomToBoundary());
dataGrid->setGridWidth(width());
dataGrid->calculate();
dataGrid->setRect(0,0,width(), height()-2- viewMargin.bottomToBoundary());
注意这里的height() - 2,因为如果直接setSceneRect(0, 0, width(), height() );你会发现虽然没有滚轮但是还是可以上下滚动,可能是QT的Bug,大概就是高度正好多了2,所以这里减去就好了。因为我们的坐标基本上都是根据View来的,所以其实这里就算不设置也无所谓。
现在,你的坐标系已经可以根据你拉扯边框来改变大小了。下一节我们再讨论把小圆点添加进去。