QT学习8:准备战斗

 cannon.h:
#ifndef CANNONFIELD_H_
#define CANNONFIELD_H_

#include <QWidget.h>

class CannonField : public QWidget
{
	Q_OBJECT
public:
	CannonField(QWidget *parent=NULL, const char *name=NULL);

	int angle() const {return ang;}
	QSizePolicy sizePolicy() const;

	public slots:
		void setAngle(int degrees);

signals:
		void angleChanged(int);

protected:
	//-------------------------------------
	// 虚函数,在update() repaint()时,界面从隐藏到显示,尺寸改变,内容改变等都会被自动
	// 调用,它本身已自动开启双缓冲,所以不会引起闪烁
	//-------------------------------------
	void paintEvent(QPaintEvent *);

private:
	int ang;
};

#endif

cannon.cpp:

#include "CannonField.h"
#include <QPainter.h>

CannonField::CannonField(QWidget *parent, const char *name)
:QWidget(parent, name)
{
	ang = 45;
	//所有Qt窗口部件都拥有一个调色板并使用它绘制自己.
	setPalette(QPalette(QColor(250, 0, 0)));
}

void CannonField::setAngle(int degrees)
{
	if (degrees<5)
	{
		degrees = 5;
	}

	if (degrees>70)
	{
		degrees = 70;
	}

	if (ang == degrees)
	{
		return;
	}

	ang = degrees;

    //--------------------------------------
	// 被调用后,立即执行重绘,因此repaint是最快的
	// 但不能放在paintEvent中调用,不然会死循环.
    // update()跟repaint()比较,update则更加有优越性。
	// update()调用之后并不是立即重绘,而是将重绘事件放入主消息循环中,
	// 由main的event loop来统一调度的(其实也是比较快的)。
	// update在调用paintEvent之前,还做了很多优化,如果update被调用了很多次,
	// 最后这些update会合并到一个大的重绘事件加入到消息队列,
	// 最后只有这个大的update被执行一次。同时也避免了repaint()中所提到的死循环
	// --  hgy notes.
	//--------------------------------------
	repaint();

	emit angleChanged(ang);
}

void CannonField::paintEvent(QPaintEvent *)
{
	// -------------------------------------
	// QPaintEvent包含一个必须被刷新的窗口部件的区域
    // QPainter默认只能在paintEvent里面调用
	// -------------------------------------
	QString s = "Angle = " + QString::number(ang);
	QPainter p(this);
	p.drawText(200, 200, s);

}

QSizePolicy CannonField::sizePolicy() const
{
	return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
}


LCDRange.h :

#ifndef LCDRANGE_H_
#define LCDRANGE_H_

#include <QWidget.h>

class QSlider;

class LCDRange:public QWidget
{
	Q_OBJECT
public:
	LCDRange(QWidget* parent=NULL, const char *name=NULL);
	int value() const;

public slots:
	void setValue(int);
	void setRange(int minVal, int maxVal);
 
signals://信号只声明,不能定义.
	void valueChanged(int);
	
private:
	QSlider       *slider;

};

#endif//LCDRANGE_H_

LCDRange.cpp:

#include "LCDRange.h"
#include <QSlider.h>
#include <QLCDNumber.h>
#include <QVBoxLayout>

LCDRange::LCDRange(QWidget* parent, const char *name)
:QWidget(parent, name)
{
	QLCDNumber *lcd = new QLCDNumber(2, this);
	slider = new QSlider(Qt::Orientation::Horizontal, this);
	slider->setRange(0, 99);
	slider->setValue(0);


	//别把display(int)写成Display(int)了,不然不会响应的,而且你就算写成display1,
	//编译器也不会报错,只是不响应
	//
	connect(slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)));

	//信号-->类的信号-->
	connect(slider, SIGNAL(valueChanged(int)), SIGNAL(valueChanged(int)));

	QVBoxLayout *vlayout = new QVBoxLayout;
	vlayout->addWidget(lcd);
	vlayout->addWidget(slider);

	setLayout(vlayout);
}

int LCDRange::value() const
{
	return slider->value();
}

void LCDRange::setValue(int value)
{
	//qWarning("setValue\n");
	slider->setValue(value);
}

void LCDRange::setRange(int minVal, int maxVal)
{
	if (minVal<0
		|| maxVal>99
		|| minVal>maxVal)
	{
		//和TRACE相似.
		qWarning("LCDRange::setRange(%d, %d)\n\t"
			"Range must be 0...99\n\t"
			"and minVal must not be greater than maxVal",
			minVal, maxVal);

		return;
	}

    slider->setRange(minVal, maxVal);
   
}


main:

#include "qtlesson.h"
#include <QtGui/QApplication>
#include <QPushButton.h>
#include <QFont.h>
#include <QVBoxLayout>
#include <QGridLayout>
#include "LCDRange.h"
#include "CannonField.h"

class MyWidget : public QWidget
{
public:
	MyWidget(QWidget *parent=NULL, const char *name=NULL);
};

MyWidget::MyWidget(QWidget* parent, const char *name)
{
	QPushButton *quit = new QPushButton("Quit", this);
	quit->setFont(QFont(("Times"), 18, QFont::Bold));

	connect(quit, SIGNAL(clicked()), qApp, SLOT(quit()));

	LCDRange *angle = new LCDRange(this);
	angle->setRange(5, 70);

	CannonField *cannonField = new CannonField(this);

	connect(angle, SIGNAL(valueChanged(int)), cannonField, SLOT(setAngle(int)));
	//connect(cannonField, SIGNAL(angleChanged(int)), angle, SLOT(setValue(int)));

	//2X2, 10 pixel border
	QGridLayout *grid = new QGridLayout(this, 2, 2, 10);

	grid->addWidget(quit, 0, 0);
	grid->addWidget(angle, 1, 0, Qt::AlignTop);
	grid->addWidget(cannonField, 1, 1);
	grid->setColStretch(1, 10);

	angle->setValue(60);

	//设置angle获得键盘焦点,这样默认情况下键盘输入会到达LCDRange窗口部件
	angle->setFocus();
}


int main(int argc, char *argv[])
{
	QApplication a(argc, argv);
	MyWidget w;
	w.setGeometry(100, 100, 500, 355);
	a.setMainWidget(&w);
	w.show();
	
	return a.exec();
}


1.paintEvent()是一个虚函数,子类可以对父类的paintEvent进行重写。当调用update(),repaint()的时候,paintEvent()会被调用,另外,当界面有任何改变的时候,paintEvent()也会被调用,这种界面的改变包括界面从隐藏到显示,界面尺寸改变,当然还包括界面内容改变的时候会被调用。paintEvent()是已经被高度优化过的函数,它本身已经自动开启并实现了双缓冲(X11系统需要手动去开启双缓冲),因此Qt中重绘不会引起任何闪烁

2.QPainter默认只能在paintEvent里面调用

原文地址:https://www.cnblogs.com/hgy413/p/3693569.html