初学QT,也一直在做一个用于嵌入式的数据采集显示的程序,自己用QFrame子类化一个用于画曲线的类Screen,能实时移动的窗口部件,当QWidget上只放一个Screen的时候,画图还正常的,当放2个或3个的时候就不正常了,而且不正常的情况还不一样,而我需要放三个,想了一天了也想不出原因,特来向大家请教一下,帮忙找找原因。下面我将程序,运行的截图,和问题一一说明:
这个是父窗口部件,其他部件都将放在他上面:
#ifndef DISPLAY_H
#define DISPLAY_H
#ifndef QT_H
#include <qwidget.h>
#endif // QT_H
class QTimer;
class Screen;
class QStringList;
class QString;
class QLineEdit;
class QPushButton;
class DisplayWidget : public QWidget {
Q_OBJECT
public:
DisplayWidget( QWidget *parent=0, const char *name=0 );
QSize sizeHint() const;
double readCurveDate();
void readFile();
void run();
protected:
//virtual void showEvent ( QShowEvent * );
private slots:
void tick();
void start();
void stop();
private:
Screen *screen1;
Screen *screen2;
Screen *screen3;
QLineEdit *lineEdit;
QPushButton *startButton, *stopButton;
QTimer *timer;
enum { Margin = 40};
QString str;
QStringList strlist;
QStringList ::Iterator it;
int time;
double yval;
};
#endif // PLOT_H
他的实现:
#include "display.h"
#include "screen.h"
#include <qlayout.h>
#include <qtimer.h>
#include <qframe.h>
#include <qlineedit.h>
#include <qstring.h>
#include <qstringlist.h>
#include <qpushbutton.h>
#include <qfile.h>
//#include <cstdlib>
#include <iostream>
using namespace std;
DisplayWidget::DisplayWidget( QWidget *parent, const char *name )
: QWidget( parent, name )
{
timer = 0;
QVBoxLayout *vbox = new QVBoxLayout( this, 10 );
QHBoxLayout *hbox = new QHBoxLayout( vbox );
screen1 = new Screen( this );
screen1->setYTitle( QObject::tr( "Vlure 1" ) );
screen2 = new Screen( this );
screen2->setYTitle( QObject::tr( "Vlure 2" ) );
screen3 = new Screen( this );
screen3->setYTitle( QObject::tr( "Vlure 3" ) );
vbox->addWidget( screen1 );
vbox->addWidget( screen2 );
vbox->addWidget( screen3 );
lineEdit = new QLineEdit(this);
lineEdit->setReadOnly( TRUE );
hbox->addWidget( lineEdit );
startButton = new QPushButton( this );
startButton->setText( tr( "&Start" ) );
startButton->setAccel( QKeySequence( tr( "Alt+S" ) ) );
stopButton = new QPushButton( this );
stopButton->setText( tr( "Sto&p" ) );
stopButton->setAccel( QKeySequence( tr( "Alt+P" ) ) );
hbox->addWidget( startButton );
hbox->addWidget( stopButton );
connect( startButton, SIGNAL( clicked () ), SLOT( start() ) );
connect( stopButton, SIGNAL( clicked () ), SLOT( stop() ) );
time = 0;
yval = 0.0;
readFile();
}
void DisplayWidget::run()
{
if ( !timer ) {
timer = new QTimer( this );
connect( timer, SIGNAL( timeout() ), SLOT( tick() ) );
}
timer->start( 1000 );
}
void DisplayWidget::tick()
{
yval = readCurveDate();
screen1->animate( yval );
screen2->animate( yval );
screen3->animate( yval );
lineEdit->setText( QString::number( yval ) );
}
void DisplayWidget::start()
{
run();
}
void DisplayWidget::stop()
{
timer->stop();
}
QSize DisplayWidget::sizeHint() const
{
return QSize( 16 * Margin, 12 * Margin );
}
//下面两个成员函数是用于将模拟数据从in.txt中读出用于显示
void DisplayWidget::readFile()
{
QFile file("in.txt");
file.open(IO_ReadOnly);
QTextStream in(&file);
str = in.read();
strlist = QStringList::split(" ", str);
it = strlist.begin();
}
double DisplayWidget::readCurveDate( )
{
QString tempStr;
double tempDate;
tempStr =(QString) *it;
tempDate = tempStr.toDouble();
if (it != strlist.end())
{
++it;
//cerr<<"time=108"<<endl;
}
else
{
it = strlist.begin();
//cerr<<"time=109"<<endl;
}
//cerr<<"curveDate ="<<tempDate<<endl;
return tempDate;
}
下面这个是用于画图的类,也是最做要的地方:
#ifndef SCREEN_H
#define SCREEN_H
#include <qframe.h>
#include <qvaluevector.h>
#include <qpixmap.h>
#include <qpainter.h>
#define FrameWidth 3
#define Step 5 //一次移动的时候的长度,五个像素
#define BaseFontHeight 20
#define BaseLineLenght 5 //画刻度的单位长度
#define SpaceMargin 5
class QRect;
class QString;
class Screen : public QFrame {
Q_OBJECT
public:
Screen( QWidget *parent=0, const char *name=0, WFlags flags=0 );
void animate( double y);
void setXTitle( QString &str );
void setYTitle( QString &str );
protected:
void initNumber( void );
void initCordinate( QPainter &p );
void updateCurve( QPainter &p );
virtual void showEvent ( QShowEvent * );
virtual void hideEvent ( QHideEvent * );
QSize minimumSize () const;
public:
double newY, oldY;
int numXTicks, numYTicks;
QValueVector< double > Yval;
bool firstShow;
int sec, min, hour;
QPixmap saveBuffer, newBuffer, midBuffer;
QRect rectCordinate;
QRect fromSaveRect;
QRect toNewRect;
QRect rectYText;
QRect rectXText;
/*We use this painter to draw evering on the newbuffer.*/
QPainter drawPainter;
QString stringYTitle;
QString stringXTitle;
};
#endif /*SCREEN_H*/
他的实现:
#include "screen.h"
#include <qevent.h>
#include <qrect.h>
#include <qsize.h>
#include <qstring.h>
#include <iostream>
using namespace std;
#include <qwmatrix.h>
#include <qfont.h>
#include <qpen.h>
Screen::Screen( QWidget *parent, const char *name, WFlags flags )
: QFrame( parent, name, flags | WNoAutoErase )
{
setLineWidth( FrameWidth );
setFrameStyle( Panel | Sunken );
setBackgroundMode( PaletteBase );
setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
firstShow = TRUE;
}
void Screen::showEvent( QShowEvent *e )
{
if ( firstShow )
initNumber();
initCordinate( drawPainter );
}
QSize Screen::minimumSize () const
{
return QSize( 20 * SpaceMargin, 20 * SpaceMargin );
}
void Screen::hideEvent( QHideEvent *e )
{
firstShow = FALSE;
}
void Screen::setXTitle( QString &str )
{
stringXTitle = str;
}
void Screen::setYTitle( QString &str )
{
stringYTitle = str;
}
void Screen::initNumber( )
{
saveBuffer.resize( size() );
saveBuffer.fill( this, 0, 0 );
newBuffer.resize( size() );
newBuffer.fill( this, 0, 0 );
midBuffer.resize( size() );
midBuffer.fill( this, 0, 0 );
drawPainter.begin(&newBuffer);
QRect newWindow = drawPainter.window();
newY = 0;
oldY =0;
sec = 0;
min = 0;
hour = 0;
stringXTitle = QObject::tr( "Time (hh:mm:ss)" );
Yval.push_back( (double)oldY );
/*曲线要在下面这个矩形里面*/
rectCordinate.setRect(
newWindow.topLeft().x()+FrameWidth + 2 * BaseFontHeight + 2 * BaseLineLenght,
newWindow.topLeft().y()+FrameWidth + 2 * SpaceMargin,
newWindow.width() - 2 * ( FrameWidth + BaseFontHeight + BaseLineLenght + SpaceMargin ),
newWindow.height() - 2 * ( FrameWidth + BaseFontHeight + BaseLineLenght + SpaceMargin ) );
/*下面这几步是让矩形的高和宽变成5个像素的倍数,是为了好看点*/
if ( 0 != ( rectCordinate.width() % (Step*Step) ) )
{
int x = rectCordinate.width() % ( Step * Step ); //( (int) sqrt( (float) Step ) );
rectCordinate.setWidth( rectCordinate.width() - x+1 );
}
if ( 0 != ( rectCordinate.height() % (Step*Step) ) )
{
int y = rectCordinate.height() % (Step*Step); rectCordinate.setHeight( rectCordinate.height() - y+1 );
}
numXTicks = rectCordinate.width() / Step;
numYTicks = rectCordinate.height() / Step;
/*rectYText是用写y轴标题的矩形范围*/
rectYText.setRect(
newWindow.topLeft().x() + FrameWidth,
newWindow.topLeft().y() + FrameWidth + 2 * SpaceMargin,
BaseFontHeight, rectCordinate.height() );
/*rectXText是用写x轴标题的矩形范围*/
rectXText.setRect(
rectCordinate.bottomLeft().x(),
newWindow.bottomLeft().y() - FrameWidth - BaseFontHeight,
rectCordinate.width(), BaseFontHeight );
/*fromSaveRect和toNewRect是用来剪切三张QPixmap上曲线的范围的矩形大小,可以显示曲线与坐标的移动*/
fromSaveRect.setRect(
rectCordinate.topLeft().x() + Step,
rectCordinate.topLeft().y() + 1,
rectCordinate.width() - Step - 1,
rectCordinate.height() + 2 * BaseLineLenght + BaseFontHeight );
toNewRect.setRect(
rectCordinate.topLeft().x() + 1,
rectCordinate.topLeft().y() + 1,
rectCordinate.width() - Step - 1,
rectCordinate.height() + 2 * BaseLineLenght + BaseFontHeight );
drawPainter.drawRect(toNewRect);
}
void Screen::initCordinate( QPainter &pCordinate )/初始化坐标系
{
if ( firstShow )
{
pCordinate.setPen( Qt::blue );
pCordinate.drawRect( rectCordinate );
/*画y轴的刻度、坐标值和标题*/
int y0 = rectCordinate.bottomLeft().y();
int x0 = rectCordinate.bottomLeft().x();
int yText = 0;
int xText= 0;
for (int j = 0; j <= numYTicks; j ++ )
{
pCordinate.drawLine( x0 - BaseLineLenght, y0, x0, y0 );
if (0 == j % Step )
{
pCordinate.drawLine( x0 - 2 * BaseLineLenght, y0, x0 - BaseLineLenght, y0 );
pCordinate.save();
pCordinate.setPen( QPen( blue, 1, DotLine) );
pCordinate.drawLine( x0 , y0, rectCordinate.bottomRight().x(), y0 );
pCordinate.restore();
pCordinate.setPen( Qt::black );
pCordinate.drawText(
x0 - 2 * BaseLineLenght - BaseFontHeight,
y0 - 2 * BaseFontHeight + 5 * Step,
BaseFontHeight, BaseFontHeight + Step,
AlignCenter , QString::number( yText) );
yText ++;
pCordinate.setPen( Qt::blue );
}
y0 -= Step;
}
/*画y轴的标题*/
pCordinate.save();
QRect tempYText(
rectYText.topLeft().x(), rectYText.topLeft().y(),
rectYText.height(), rectYText.height() );
pCordinate.setViewport( tempYText );
QRect rectYViewport = pCordinate.viewport();
pCordinate.setWindow( -(int)rectYViewport.width()/2, -(int)rectYViewport.height()/2,
rectYViewport.width(), rectYViewport.height() );
QRect rectYWindow = pCordinate.window();
QRect rectDrawText(
rectYWindow.topLeft().x(),
-(int)rectYText.width()/2,
rectYText.height(),
rectYText.width() );
pCordinate.rotate(-90.0);
double dy = ( rectYWindow.width() - rectDrawText.height() ) / 2;
dy = dy > 0 ? dy : ( -dy );
pCordinate.translate( 0, -dy );
pCordinate.drawText(
rectDrawText.topLeft().x(),
rectDrawText.topLeft().y(),
rectDrawText.width(),
rectDrawText.height(),
AlignCenter, stringYTitle );
pCordinate.restore();
/*画x轴的刻度和标题*/
pCordinate.setPen( Qt::blue );
y0 = rectCordinate.bottomLeft().y();
for ( int i = 0; i <= numXTicks; i ++ )
{
pCordinate.drawLine( x0 , y0, x0, y0 + BaseLineLenght );
if ( 0 == i % (2*Step) )
{
pCordinate.save();
pCordinate.setPen( QPen( blue, 1, DotLine) );
pCordinate.drawLine( x0 , rectCordinate.bottomLeft().y(),
x0 , rectCordinate.topLeft().y() );
pCordinate.restore();
}
x0 += Step;
}
/*画x轴的标题*/
pCordinate.drawText(
rectXText.topLeft().x(), rectXText.topLeft().y(),
rectXText.width(), rectXText.height(),
AlignCenter, stringXTitle );
}
/*将初始化好的坐标系复制到Screen部件上*/
bitBlt( this, 0, 0, &newBuffer, 0, 0, newBuffer.width(), newBuffer.height() );
}
void Screen::animate( double y )
{
newY = y;
if ( (int) Yval.size() <= (int) width() / 4 )
{
Yval.append( newY );
} else {
Yval.erase( Yval.begin() );
Yval.append( newY );
}
updateCurve( drawPainter);
}
/*更新曲线,实现曲线和坐标的移动*/
void Screen::updateCurve( QPainter &pDrawCurve)
{
/*设计思路:三个QPixmap,其中newBuffer是用于画图和复制给Screen的,saveBuffer是用于保存上次newBuffer,midBuffer从saveBuffer中将需要往前移动的部分剪切到自己上面,最后newBuffer从midBuffer上将移动的部分加上需要的空白复制到自己绘图矩形内,实现了曲线和坐标向前移动,最后在空白出画上最新的坐标,曲线和时间*/
copyBlt ( &saveBuffer, 0, 0,&newBuffer, 0, 0, newBuffer.width(), newBuffer.height() );
copyBlt ( &midBuffer, toNewRect.topLeft().x(), toNewRect.topLeft().y(),
&saveBuffer, fromSaveRect.topLeft().x(), fromSaveRect.topLeft().y(),
fromSaveRect.width(), fromSaveRect.height() );
copyBlt ( &newBuffer, rectCordinate.topLeft().x()+1, rectCordinate.topLeft().y()+1,
&midBuffer, rectCordinate.topLeft().x()+1, rectCordinate.topLeft().y()+1,
rectCordinate.width()-2, fromSaveRect.height() );
QValueVector<double>::iterator Yit = Yval.end();
double Ynew, Yold;
int Xnew, Xold;
Ynew = rectCordinate.bottomRight().y() - *(--Yit) - 1;
Xnew = rectCordinate.bottomRight().x() -1;
Yold = rectCordinate.bottomRight().y() - *(--Yit) - 1;
Xold = rectCordinate.bottomRight().x() - Step;
/*画移动后空白处的横坐标和刻度*/
pDrawCurve.setPen( Qt::blue );
pDrawCurve.drawLine(
toNewRect.bottomRight().x(), rectCordinate.bottomRight().y(),
rectCordinate.bottomRight().x(), rectCordinate.bottomRight().y() ); pDrawCurve.drawLine(
toNewRect.bottomRight().x(), rectCordinate.bottomRight().y(),
toNewRect.bottomRight().x(), rectCordinate.bottomRight().y() + BaseLineLenght );
/*draw the dotline in the horizontal direction*/
int y0 = rectCordinate.bottomRight().y();
static bool drawDotLine = FALSE;
pDrawCurve.save();
if ( drawDotLine )
{
for (int j =0; j < (numYTicks /5 -1 ); j++)
{
y0 -= 5*Step;
pDrawCurve.setPen( QPen( blue, 1, DotLine) );
pDrawCurve.drawLine( toNewRect.bottomRight().x() , y0,
rectCordinate.bottomRight().x(), y0 );
}
}
pDrawCurve.restore();
drawDotLine = !drawDotLine;
/*draw the calibration values of x-axis*/
static int numX = 0;
if ( 0 == numX % (2*Step) )
{
/*以hh:mm:ss的格式画时间*/
int low = hour % 10;
int high = hour / 10;
QString timeString;
timeString += ( QString( "%1%2:").arg(high).arg(low) );
low = min % 10;
high = min / 10;
timeString += ( QString( "%1%2:").arg(high).arg(low) );
low = sec % 10;
high = sec / 10;
timeString += ( QString( "%1%2").arg(high).arg(low) );
/*draw the long calibration */
pDrawCurve.drawLine(
toNewRect.bottomRight().x(),
rectCordinate.bottomRight().y() + BaseLineLenght,
toNewRect.bottomRight().x(),
rectCordinate.bottomRight().y() + 2 * BaseLineLenght );
/*draw the dotline in the vertical direction*/
pDrawCurve.save();
pDrawCurve.setPen( QPen( blue, 1, DotLine) );
pDrawCurve.drawLine(
toNewRect.bottomRight().x(),
rectCordinate.bottomRight().y(),
toNewRect.topRight().x(),
rectCordinate.topRight().y() );
pDrawCurve.restore();
/*draw the calibration values of x-axis*/
if ( 0 == numX % (4*Step) )
{
pDrawCurve.drawLine(
toNewRect.bottomRight().x(),
rectCordinate.bottomRight().y() + 2*BaseLineLenght,
toNewRect.bottomRight().x(),
rectCordinate.bottomRight().y() + 3 * Step );
pDrawCurve.setPen( Qt::black );
/*画时间的矩形范围*/
QRect rectCValue(
toNewRect.bottomRight().x() - 9 * Step,
toNewRect.bottomRight().y() - BaseFontHeight+2,
10 * Step,
BaseFontHeight);
pDrawCurve.drawText(
rectCValue.topLeft().x(),
rectCValue.topLeft().y(),
rectCValue.width(),
rectCValue.height(),
AlignRight | Qt::AlignHCenter,
timeString );
}
sec += 10;
if ( 60 == sec )
{
sec = 0;
min += 1;
if ( 60 == min )
{
min = 0;
hour += 1;
if ( 60 == hour )
hour = 0;
}
}
}
numX ++;
if ( numX >= 100 )
numX = 0;
/*更新曲线,便且将曲线复制到Screen上显示*/
pDrawCurve.setPen( Qt::black );
pDrawCurve.drawLine( Xold, (int)Yold, Xnew, (int)Ynew );
bitBlt( this, 0, 0, &newBuffer, 0, 0, newBuffer.width(), newBuffer.height() );
}
碰到的问题有下面几个:
第一:如图一所示刚运行的时候不能显示我在Screen::initCordinate( QPainter &pCordinate )里初始化的图,只有当按了Start后才能显示,(我在Screen::showEvent( QShowEvent *e )里初始化变量和坐标系,是因为我在构造函数中初始化的时候调用width()和height()的时候值是负的,显然不对)原来我timer是在构造函数的最后开始自动运行的时候就可以?
第二:如剩余的图所示,我只放一个Screen的时候还好的,这也是我要的结果,但放两个的时候就出错了,很多都没有画出来而且上面的那个刻度间隔也不对,我是以20个为一个单位。放三个的时候screen1是对的,但screen2和screen3的坐标值和刻度都错了,为什么同一个类会出现这种情况??
我实在是找不出原因,只好求救于大家,希望能指导一下小弟,不胜感激