Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 74 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
74
Dung lượng
369,37 KB
Nội dung
277 Chapter 12 ✦ Drawing and Painting with QPainter 13 } 14 DrawPixel::DrawPixel(QWidget *parent,const 15 char *name) : QWidget(parent,name) 16 { 17 setFixedSize(400,200); 18 } 19 void DrawPixel::paintEvent(QPaintEvent *) 20 { 21 QPainter p(this); 22 p.setPen(QColor(“white”)); 23 for(int x=20; x<400; x += 20) { 24 for(int y=20; y<200; y += 20) { 25 p.drawPoint(x-1,y); 26 p.drawPoint(x+1,y); 27 p.drawPoint(x,y-1); 28 p.drawPoint(x,y+1); 29 } 30 } 31 p.setPen(QColor(“red”)); 32 for(double x=0; x<400; x++) { 33 double y = sin(x / 30); 34 y *= x / 4; 35 y += 100; 36 p.drawPoint((int)x,(int)y); 37 } 38 } The paintEvent() method beginning on line 19 draws the grid points and the curve. Points, by the way, are drawn with the QPen, normally used to draw lines. You can think of a pixel as the shortest of all possible lines. Line 22 calls setPen() to establish a white pen for drawing the points, and line 31 calls setPen() to estab- lish the red pen for drawing the dots making up the curve. The loop on lines 23 through 30 draws the collection of white points shown in Figure 12-9. The points are drawn at 20-pixel intervals both vertically and horizon- tally. Each point is drawn as four pixels — one above and one to each side of the center point. The loop on lines 32 through 37 draws a sine wave that increases in amplitude from left to right. The variables x and y are declared as double to simplify the calcula- tions. The window is fixed at 400 pixels wide, so the value of x varies from 0 to 400, resulting in one painted pixel in each of the 400 “pixel columns.” Line 33 calculates the sine, treating the value of x as a number of radians (using a divisor other than 30 here will change the number of cycles that appear in the window). Line 34 multiplies the y value such that its magnitude becomes larger as x becomes larger. Line 35 adds 100 to the y value so it will be vertically centered in the window. The call to drawPoint() on line 36 paints the pixel. 4682-1 ch12.f.qc 11/13/00 14:12 Page 277 278 Part II ✦ Step by Step Drawing Arrays of Pixels In the previous example, all of the points were calculated each time the window was painted. Sometimes it is more convenient to calculate the points only once, or load them from a file and store them in an array. The following example displays the same window as the previous example, shown in Figure 12-9, but it calculates the pixel locations only once and stores them in an array. DrawPixel2 Header 1 /* drawpixel2.h */ 2 #ifndef DRAWPIXEL_H 3 #define DRAWPIXEL_H 4 5 #include <qwidget.h> 6 #include <qpointarray.h> 7 8 class DrawPixel2: public QWidget 9 { 10 public: 11 DrawPixel2(QWidget *parent=0,const char *name=0); 12 private: 13 QPointArray *grid; 14 QPointArray *curve; 15 protected: 16 virtual void paintEvent(QPaintEvent *); 17 }; 18 19 #endif Lines 13 and 14 declare pointers to a pair of QPointArray objects. The one named curve is used to contain the points defining the trace, and the one named grid will contain the locations of the white points in the background. DrawPixel2 1 /* drawpixel2.cpp */ 2 #include <kapp.h> 3 #include <qpainter.h> 4 #include “drawpixel2.h” 5 6 int main(int argc,char **argv) 7 { 8 KApplication app(argc,argv,”drawpixel2”); 9 DrawPixel2 drawpixel2; 10 drawpixel2.show(); 11 app.setMainWidget(&drawpixel2); 12 return(app.exec()); 13 } 4682-1 ch12.f.qc 11/13/00 14:12 Page 278 279 Chapter 12 ✦ Drawing and Painting with QPainter 14 DrawPixel2::DrawPixel2(QWidget *parent,const 15 char *name) : QWidget(parent,name) 16 { 17 int index; 18 setFixedSize(400,200); 19 20 grid = new QPointArray(4 * 20 * 10); 21 index = 0; 22 for(int x=20; x<400; x += 20) { 23 for(int y=20; y<200; y += 20) { 24 grid->setPoint(index++,x-1,y); 25 grid->setPoint(index++,x+1,y); 26 grid->setPoint(index++,x,y-1); 27 grid->setPoint(index++,x,y+1); 28 } 29 } 30 curve = new QPointArray(400); 31 index = 0; 32 for(double x=0; x<400; x++) { 33 double y = sin(x / 30); 34 y *= x / 4; 35 y += 100; 36 curve->setPoint(index++,(int)x,(int)y); 37 } 38 } 39 void DrawPixel2::paintEvent(QPaintEvent *) 40 { 41 QPainter p(this); 42 p.setPen(QColor(“white”)); 43 p.drawPoints(*grid); 44 p.setPen(QColor(“red”)); 45 p.drawPoints(*curve); 46 } The constructor, beginning on line 14, does all of the calculation work and stores the result in the arrays. The call to setFixedSize() on line 18 prohibits the win- dow from being resized. The QPointArray object to contain the grid points is created on line 20. There is one entry in the array for each of the points, so the total size of the array is the product of 4 (the number of pixels in each grid point), 20 (the number of grid points that will appear along the x axis), and 10 (the number of grid points that will appear along the y axis). The loop on lines 22 through 29 inserts four pixel locations for each of the grid points. The QPointArray object to contain the trace of the curve is created on line 30. The calculations, and the number of points, are the same as they were in the previous example. There are 400 points calculated, and all 400 are stored in the array by the call to setPoint() on line 36. 4682-1 ch12.f.qc 11/13/00 14:12 Page 279 280 Part II ✦ Step by Step The paintEvent() method starting on line 39 has much less to do than in the previous example. A QPainter object is created, a white pen is used to draw the points defined in grid, and a red pen is used to draw the points in curve. Sometimes you need to recalculate the values under some circumstances, but not under others. For example, if you wish to recalculate the values only when the win- dow changes size, the top of your paintEvent() method —using the values in QPaintDeviceMetrics —determines whether the window size has changed and, if so, calls the method that does the calculation. Vector Line Drawing Two methods can be used to implement vector drawing. They don’t do anything that can’t be done with drawLine(), but they can be very convenient in the cre- ation of certain kinds of drawings. The methods moveTo() and lineTo() are really left over from the days when graphics were done using a pen plotter. Both methods move the pen from one location to another, but only one of them holds the pen down, causing a line to be drawn. The pen always has a position, so in order to draw a line, it is only necessary to specify the other end of the line. Once the line has been drawn, the pen assumes the new position. The following example reads the drawing instructions from a file and uses them to display the graphic shown in Figure 12-10. Each line of the input text file contains an opcode ( m for move and d for draw) and the coordinate point for the action to take place. The file used in this example starts like this: m 60 110 d 60 10 d 160 10 d 160 60 m 160 80 d 160 180 . . . The first line is an instruction to move to the point (60,110). The second command will draw a line from the pen’s position at (60,110) to a new location at (60,10). Figure 12-10: A line drawing defined in a file 4682-1 ch12.f.qc 11/13/00 14:12 Page 280 281 Chapter 12 ✦ Drawing and Painting with QPainter 1 /* drawvector.cpp */ 2 #include <kapp.h> 3 #include <qpainter.h> 4 #include <stdio.h> 5 #include “drawvector.h” 6 7 int main(int argc,char **argv) 8 { 9 KApplication app(argc,argv,”drawvector”); 10 DrawVector drawvector; 11 drawvector.show(); 12 app.setMainWidget(&drawvector); 13 return(app.exec()); 14 } 15 DrawVector::DrawVector(QWidget *parent,const 16 char *name) : QWidget(parent,name) 17 { 18 setFixedSize(230,190); 19 } 20 void DrawVector::paintEvent(QPaintEvent *) 21 { 22 FILE *fd; 23 char code[20]; 24 int x; 25 int y; 26 27 if((fd = fopen(“points.dat”,”r”)) != NULL) { 28 QPainter p(this); 29 while(fscanf(fd,”%s %d %d”,code,&x,&y) == 3) { 30 if(code[0] == ‘m’) 31 p.moveTo(x,y); 32 else if(code[0] == ‘d’) 33 p.lineTo(x,y); 34 } 35 fclose(fd); 36 } 37 } All of the drawing is done in the loop on lines 28 through 34. Line 28 initializes graphic operations by creating a QPainter object for this widget. The call to fscanf() on line 29 reads a line of input data—the command, the x coordinate, and the y coordinate. If the command is to move the current cursor, the method moveTo() is called on line 31. If the command is to draw a line from the current cursor to this new location, a call is made to lineTo() on line 33. 4682-1 ch12.f.qc 11/13/00 14:12 Page 281 282 Part II ✦ Step by Step Line Segments and Polygons Some QPainter methods allow you to store a set of points in a QPointArray object and then use the points to draw polygons. The following program demonstrates some of the different ways a collection of line segments can be drawn: 1 /* drawpoly.cpp */ 2 #include <kapp.h> 3 #include <qpainter.h> 4 #include “drawpoly.h” 5 6 int main(int argc,char **argv) 7 { 8 KApplication app(argc,argv,”drawpoly”); 9 DrawPoly drawpoly; 10 drawpoly.show(); 11 app.setMainWidget(&drawpoly); 12 return(app.exec()); 13 } 14 DrawPoly::DrawPoly(QWidget *parent,const 15 char *name) : QWidget(parent,name) 16 { 17 setFixedSize(500,100); 18 } 19 void DrawPoly::paintEvent(QPaintEvent *) 20 { 21 int offset = 0; 22 QPointArray parray(10); 23 QPainter p(this); 24 25 setPoints(parray,offset); 26 p.drawLineSegments(parray); 27 28 setPoints(parray,offset += 100); 29 p.drawPolyline(parray); 30 31 setPoints(parray,offset += 100); 32 p.drawPolygon(parray); 33 34 p.setBrush(QColor(“white”)); 35 setPoints(parray,offset += 100); 36 p.drawPolygon(parray,TRUE); 37 38 setPoints(parray,offset += 100); 39 p.drawPolygon(parray,FALSE); 40 } 41 void DrawPoly::setPoints(QPointArray &parray,int offset) 42 { 43 parray.setPoint(0,10+offset,50); 44 parray.setPoint(1,70+offset,50); 45 parray.setPoint(2,70+offset,30); 4682-1 ch12.f.qc 11/13/00 14:12 Page 282 283 Chapter 12 ✦ Drawing and Painting with QPainter 46 parray.setPoint(3,50+offset,30); 47 parray.setPoint(4,50+offset,90); 48 parray.setPoint(5,30+offset,90); 49 parray.setPoint(6,30+offset,10); 50 parray.setPoint(7,90+offset,10); 51 parray.setPoint(8,90+offset,70); 52 parray.setPoint(9,10+offset,70); 53 } The setPoints() method on line 41 inserts the points into the array. The same set of points is used for each drawing, as shown in Figure 12-11, except the horizontal position is shifted to the right by the amount of the offset. Figure 12-11: Five ways to draw a polygon The call to drawLineSegments() on line 26 draws the version of the polygon shown on the far left of Figure 12-11. The lines are not joined together because only line segments are drawn. That is, the first line is drawn between point[0] and point[1], the second is drawn between point[2] and point[3], and so on. For every line drawn, there must be two members in the array of points. Of course, you can force the lines to join into a polygon by using the ending point of a line as the start- ing point of the next. The call to drawPolyLine() on line 32 uses the same input information as draw LineSegments() , but it draws all the line segments by starting each new line seg- ment at the point where the previous line segment left off. That is, the first line is drawn between point[0] and point[1], the second is drawn between point[1] and point[2], and so on. In the array of point data, the last point does not coincide with the first point, so the polygon is not closed. The call to drawPolygon() on line 32 draws the figure in the same way as drawLine Sgemetns() , but it also draws a line from the end point back to the beginning, resulting in a closed shape. The call to drawPolygon() on line 36 draws the shape after a QBrush has been stored in the QPainter object, and this results in the polygon being filled. Just as with any of the other shapes, the area is filled before it is outlined, causing the out- lining to appear on top of the fill. The second argument to the method call sets the winding rule to TRUE, which means that all areas of the polygon will be filled with- out regard to overlaps of itself. 4682-1 ch12.f.qc 11/13/00 14:12 Page 283 284 Part II ✦ Step by Step The call to drawPolygon() on line 39 is the same as the previous one, except the winding rule is set to FALSE. This setting means that the only regions of the poly- gons that are filled are those covered with an odd number of layers. The rightmost drawing in Figure 12-11 shows that the area where the shape overlaps itself is not filled —that is, there are two layers of the shape at the overlap point. If the shape were to overlap the same point with a third layer, it would be filled again. Ellipses and Circles The method drawEllipse() is used to render both circles and ellipses because a circle is simply an ellipse with equal height and width. The following program displays the window shown in Figure 12-12, containing two ellipses and a circle: 1 /* drawellipse.cpp */ 2 #include <kapp.h> 3 #include <qpainter.h> 4 #include “drawellipse.h” 5 6 int main(int argc,char **argv) 7 { 8 KApplication app(argc,argv,”drawellipse”); 9 DrawEllipse drawellipse; 10 drawellipse.show(); 11 app.setMainWidget(&drawellipse); 12 return(app.exec()); 13 } 14 DrawEllipse::DrawEllipse(QWidget *parent,const 15 char *name) : QWidget(parent,name) 16 { 17 setFixedSize(260,140); 18 } 19 void DrawEllipse::paintEvent(QPaintEvent *) 20 { 21 QPainter p(this); 22 23 p.drawEllipse(10,50,110,40); 24 p.setBrush(QColor(“white”)); 25 p.drawEllipse(130,25,90,90); 26 p.setPen(NoPen); 27 p.drawEllipse(230,10,20,120); 28 } Figure 12-12: Two ellipses and a circle 4682-1 ch12.f.qc 11/13/00 14:12 Page 284 285 Chapter 12 ✦ Drawing and Painting with QPainter The drawEllipse() method requires that you define a bounding box to specify the four sides of the ellipse. The bounding box is defined by the x and y coordinates of its upper-left corner, and the width and height of the box. For example, the ellipse on the left in Figure 12-12 is drawn by the call to drawEllipse() on line 23, with its upper-left corner 10 pixels from the left edge and 50 pixels from the top. The width of the ellipse is 110 pixels and its height is 40 pixels. A QBrush object is added to QPainter by the call to setBrush() on line 24, so the rest of the ellipses are filled with the brush color. Line 26 calls setPen() to remove the pen from QPainter, so the ellipse on the right has no outline. It may happen that you need to draw a circle or an ellipse around a center point instead of the upper left corner. To do this, simply subtract the radius from the center point (in each direction) to locate the upper-left corner: p.drawEllipse(x - (w / 2),y - (h / 2),w,h); Drawing Parts of Circles and Ellipses There are three ways you can draw part of a circle or an ellipse. The process is the same as drawing a circle or ellipse, as in the previous example, except you must also specify a starting and ending angle. To specify which part of the circle or ellipse is to be drawn, it is necessary to spec- ify the starting and ending angles. The angles are measured in units of one-sixteenth of a degree. If you are going to be entering hard-coded angles, Table 12-2 lists some of the more commonly used values. Table 12-2 Comparison of Angle Measurement Units Qt Units Degrees Radians 00 0 720 45 0.7854 1440 90 1.5708 2160 135 2.3562 2880 180 3.1416 3600 225 3.9270 4320 270 4.7124 5040 315 5.4978 5760 360 6.2832 4682-1 ch12.f.qc 11/13/00 14:12 Page 285 286 Part II ✦ Step by Step If you are going to be calculating the angles, most math software utilities use either degrees or radians; you will need to convert back and forth. The following state- ments will convert degrees and radians to the Qt scale: angle = degree * 16; angle = (radian * 180) / PI; And these statements will convert Qt scale values to degrees and radians: degree = angle / 16; radian = (angle * PI) / 180; Positive rotation is counterclockwise. The zero-degree point is on the right. The starting and ending angles are expressed in relative terms. That is, the starting angle specifies the distance from the zero point that the drawing is to begin, and the ending angle specifies the distance from the starting angle to the end of the drawing. Both numbers can be either positive or negative. If the starting angle is less than the ending angle, the drawing occurs in the positive (counterclockwise) direction. If the starting angle is less than the ending angle, the drawing occurs in the negative (clockwise) direction. The following example demonstrates three different approaches to drawing an arc: 1 /* arcpiechord.cpp */ 2 #include <kapp.h> 3 #include <qpainter.h> 4 #include “arcpiechord.h” 5 6 int main(int argc,char **argv) 7 { 8 KApplication app(argc,argv,”arcpiechord”); 9 ArcPieChord arcpiechord; 10 arcpiechord.show(); 11 app.setMainWidget(&arcpiechord); 12 return(app.exec()); 13 } 14 ArcPieChord::ArcPieChord(QWidget *parent,const 15 char *name) : QWidget(parent,name) 16 { 17 setFixedSize(260,420); 18 } 19 void ArcPieChord::paintEvent(QPaintEvent *) 20 { 21 QPainter p(this); 22 23 p.drawArc(10,50,110,40,0,4000); 24 p.drawChord(10,190,110,40,0,4000); 25 p.drawPie(10,330,110,40,0,4000); 26 p.setBrush(QColor(“white”)); 4682-1 ch12.f.qc 11/13/00 14:12 Page 286 [...]... p.drawRoundRect(10,10 ,50 ,50 ); p.drawText(30, 35, ”1”); p.drawRoundRect(70,10 ,50 ,50 ,50 ,50 ); p.drawText(90, 35, ”2”); p.drawRoundRect(130,10 ,50 ,50 ,100,100); p.drawText( 150 , 35, ”3”); p.drawRoundRect(10,70,170 ,50 ); 4682-1 ch12.f.qc 11/13/00 14:12 Page 289 Chapter 12 ✦ Drawing and Painting with QPainter 34 35 36 37 38 39 40 41 42 43 44 45 46 47 } p.drawText(90, 95, ”4”); p.drawRoundRect(10,130,170 ,50 ,0 ,50 ); p.drawText(90, 155 , 5 );... setFixedSize( 450 , 250 ); printButton = new QPushButton(“Printer Setup”,this); printButton->setGeometry(340,200,90,40); connect(printButton,SIGNAL(clicked()), this,SLOT(printSetupSlot())); } void PrintMetrics::printSetupSlot() 4682-1 ch13.f.qc 11/13/00 14:13 Page 303 Chapter 13 ✦ Graphics Manipulation 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61... p.setBrush(QColor(“white”)); p.drawEllipse( 25, 25, 250 , 150 ); p.setBrush(QBrush(QColor(“black”),Qt::VerPattern)); p.setClipRect(30,30,70,70); p.drawEllipse( 25, 25, 250 , 150 ); p.setBrush(QBrush(QColor(“black”),Qt::Dense5Pattern)); QPointArray pa; pa.setPoints(3,100,140,200 ,50 ,220,180); QRegion region(pa); p.setClipRegion(region); p.drawEllipse( 25, 25, 250 , 150 ); } The call to setBrush() on line 25 and the call to drawEllipse()... paint.drawRect(100,100, 250 ,50 ); paint.end(); } void PrintGraphic::printSlot() { 299 4682-1 ch13.f.qc 300 11/13/00 14:13 Page 300 Part II ✦ Step by Step 39 40 41 42 43 44 45 46 47 48 49 50 51 52 } QPainter paint; QPrinter printer; if(printer.setup(this)) { paint.begin(&printer); paint.setBrush(QColor(“black”)); paint.drawRect (50 , 75, 350 ,100); paint.setBrush(QColor(“white”)); paint.drawEllipse( 150 ,50 , 150 , 150 ); paint.setPen(QWidget::NoPen);... { setFixedSize( 450 , 250 ); printButton = new QPushButton(“Print”,this); printButton->setGeometry(370,200,70,40); connect(printButton,SIGNAL(clicked()), this,SLOT(printSlot())); } void PrintGraphic::paintEvent(QPaintEvent *) { QPainter paint; paint.begin(this); paint.setBrush(QColor(“black”)); paint.drawRect (50 , 75, 350 ,100); paint.setBrush(QColor(“white”)); paint.drawEllipse( 150 ,50 , 150 , 150 ); paint.setPen(QWidget::NoPen);... p.setViewport(0,0,100 ,50 ); paintFigure(p); p.setViewport(100,0,200 ,50 ); paintFigure(p); p.setViewport(0 ,50 ,100, 150 ); paintFigure(p); p.setViewport(100 ,50 ,200, 150 ); paintFigure(p); 4682-1 ch13.f.qc 11/13/00 14:13 Page 309 Chapter 13 ✦ Graphics Manipulation 30 31 32 33 34 35 36 37 38 39 40 41 } void FitSubWindow::paintFigure(QPainter &p) { p.setWindow(0,0,300,300); p.setBrush(QColor(“white”)); p.drawRoundRect (50 ,50 ,200,200,30,30);... Page 296 Part II ✦ Step by Step 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include #include int main(int argc,char **argv) { KApplication app(argc,argv,”record”); QPainter paint; QPicture pic; paint.begin(&pic); paint.setBrush(QColor(“black”)); paint.drawRect (50 , 75, 350 ,100); paint.setBrush(QColor(“white”)); paint.drawEllipse( 150 ,50 , 150 , 150 ); paint.setPen(QWidget::NoPen);... 44 45 46 47 } p.drawText(90, 95, ”4”); p.drawRoundRect(10,130,170 ,50 ,0 ,50 ); p.drawText(90, 155 , 5 ); p.drawRoundRect(10,190,170 ,50 ,50 ,80); p.drawText(90,2 15, ”6”); p.drawRoundRect(10, 250 ,170 ,50 ,100,100); p.drawText(90,2 75, ”7”); p.drawRoundRect(10,310,170 ,50 ,9,30); p.drawText(90,3 35, ”8”); Figure 12-14: Some of the many forms of rounded rectangles Calling one of the following two methods draws a rounded rectangle:... FitWindow::paintEvent(QPaintEvent *) { QPainter p(this); p.setWindow(0,0,300,300); p.drawRoundRect (50 ,50 ,200,200,30,30); p.setBrush(QColor(“black”)); p.drawEllipse(100,100,100,100); 4682-1 ch13.f.qc 11/13/00 14:13 Page 307 Chapter 13 ✦ Graphics Manipulation 23 24 25 26 } p.setBrush(QColor(“white”)); p.drawPie (50 ,50 ,100,100,270*16,90*16); p.drawPie( 150 , 150 ,100,100,90*16,90*16); The call to setWindow() on line 18 establishes the... fm.height(); string = “Page height: “; 303 4682-1 ch13.f.qc 304 11/13/00 14:13 Page 304 Part II ✦ Step by Step 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 1 05 106 107 108 109 110 111 112 113 114 1 15 116 117 118 119 120 121 122 123 124 1 25 126 127 128 } string += QString::number(metrics.height()); paint.drawText(x,y,string); y += fm.height(); if(printer.fullPage()) . p.drawRoundRect(10,10 ,50 ,50 ); 25 p.drawText(30, 35, ”1”); 26 27 p.drawRoundRect(70,10 ,50 ,50 ,50 ,50 ); 28 p.drawText(90, 35, ”2”); 29 30 p.drawRoundRect(130,10 ,50 ,50 ,100,100); 31 p.drawText( 150 , 35, ”3”); 32 33. Radians 00 0 720 45 0.7 854 1440 90 1 .57 08 2160 1 35 2. 356 2 2880 180 3.1416 3600 2 25 3.9270 4320 270 4.7124 50 40 3 15 5.4978 57 60 360 6.2832 4682-1 ch12.f.qc 11/13/00 14:12 Page 2 85 286 Part II ✦. p.drawRoundRect(10,190,170 ,50 ,50 ,80); 40 p.drawText(90,2 15, ”6”); 41 42 p.drawRoundRect(10, 250 ,170 ,50 ,100,100); 43 p.drawText(90,2 75, ”7”); 44 45 p.drawRoundRect(10,310,170 ,50 ,9,30); 46 p.drawText(90,3 35, ”8”); 47