#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里面调用