首页 » PHP和MySQL Web开发(原书第4版) » PHP和MySQL Web开发(原书第4版)全文在线阅读

《PHP和MySQL Web开发(原书第4版)》22.6 绘制图像与用图表描绘数据

关灯直达底部

在上一个应用程序中,我们已经使用了已有图像和文本。我们还没有了解绘图的例子,现在就介绍它。

在这个例子中,我们将在本网站中进行一次民意测验,让用户为虚构的选举进行投票。投票结果保存到一个MySQL数据库中,并用图像函数绘制出条形图,以表示投票结果。

这些函数的另一个主要应用是绘制图形。还可以用图形来表示任何需要表示的数据——交易量、网站点击数和任何我们所喜欢的。

对于这个例子,我们花了几分钟建立了一个名为poll的MySQL数据库。该数据库包含一个名为poll_results的表,该表的candidate列保存了候选人的名字,num_votes列保存了候选人收到的投票数。我们还为该数据库创建了一个名为poll的用户,其密码为poll。这个表的创建过程简单明了,可以通过运行如程序清单22-3所示的SQL脚本完成。可以使用如下命令,以root用户的身份进行登录:

mysql-u root-p<pollsetup.sql

当然,也可以以具有适当权限的用户身份进行登录。

程序清单22-3 pollsetup.sql——创建Poll数据库

create database poll;

use poll;

create table poll_results(

candidate varchar(30),

num_votes int

);

insert into poll_results values

(/'John Smith/',0),

(/'Mary Jones/',0),

(/'Fred Bloggs/',0)

;

grant all privileges

on poll.*

to [email protected]

identified by/'poll/';

该数据库包含了三个候选人。我们通过vote.html页面提供一个投票界面。该页面代码如程序清单22-4所示。

程序清单22-4 vote.html——用户可以在此投票

<html>

<head>

<title>Polling</title>

<head>

<body>

<h1>Pop Poll</h1>

<p>Who will you vote for in the election?</p>

<form method=/"post/"action=/"show_poll.php/">

<input type=/"radio/"name=/"vote/">John Smith<br/>

<input type=/"radio/"name=/"vote/">Mary Jones<br/>

<input type=/"radio/"name=/"vote/">Fred Bloggs<br/><br/>

<input type=/"submit/">

</form>

</body>

</html>

该页面的输出结果如图22-7所示。

图 22-7 用户可以在此投票,点击提交按钮将显示当前投票结果

通常的想法是,当用户点击提交按钮时,将他们的投票添加到数据库中,然后再将数据库中所有的投票取出,绘制当前结果的条形图。

在用户投票之后的条形图输出如图22-8所示。

图 22-8 投票结果通过将一系列线条、矩形和文本绘制到背景图来实现

创建该图像的脚本非常长。我们将它分为4部分,并分别加以讨论。这些脚本大多数都是我们非常熟悉的;我们已经看过许多类似的MySQL示例。我们已经了解了如何用单一的颜色绘制一个背景画布,以及如何在背景图上打印文本标签。

与前面所介绍的脚本相比较,该脚本新的部分是绘制线条和矩形。我们将重点讨论这些部分。第1部分(脚本1个部分的第1部分)如程序清单22-5-1所示。

第1部分,如程序清单22-5-1所示,将连接到MySQL数据库,根据用户的投票更新投票数,获得新票数。在获得新票数信息后,就可开始通过计算绘制图表。第2部分代码如程序清单22-5-2所示。

程序清单22-5-1 show_poll.php——脚本的第1部分将更新投票数据库并检索新的投票结果

<?php

/*******************************************

Database query to get poll info

*******************************************/

//get vote from form

$vote=$_REQUEST[/'vote/'];

//log in to database

if(!$db_conn=new mysqli(/'localhost/',/'poll/',/'poll/',/'poll/'))

{

echo/'Could not connect to db<br/>/';

exit;

}

if(!empty($vote))//if they filled the form out,add their vote

{

$vote=addslashes($vote);

$query=/"update poll_results

set num_votes=num_votes+1

where candidate=/'$vote/'/";

if(!([email protected]$db_conn->query($query)))

{

echo/'Could not connect to db<br/>/';

exit;

}

}

//get current results of poll,regardless of whether they voted

$query=/'select*from poll_results/';

if(!([email protected]$db_conn->query($query)))

{

echo/'Could not connect to db<br/>/';

exit;

}

$num_candidates=$result->num_rows;

//calculate total number of votes so far

$total_votes=0;

while($row=$result->fetch_object)

{

$total_votes+=$row->num_votes;

}

$result->data_seek(0);//reset result pointer

程序清单22-5-2 show_poll.php——脚本的第2部分设置绘画所需要的所有变量

/*******************************************

Initial calculations for graph

*******************************************/

//set up constants

putenv(/'GDFONTPATH=C:WINDOWSFonts/');

$width=500;//width of image in pixels-this will fit in 640x480

$left_margin=50;//space to leave on left of graph

$right_margin=50;//ditto right

$bar_height=40;

$bar_spacing=$bar_height/2;

$font=/'arial/';

$title_size=16;//point

$main_size=12;//point

$small_size=12;//point

$text_indent=10;//position for text labels from edge of image

//set up initial point to draw from

$x=$left_margin+60;//place to draw baseline of the graph

$y=50;//ditto

$bar_unit=($width-($x+$right_margin))/100;//one/"point/"on the graph

//calculate height of graph-bars plus gaps plus some margin

$height=$num_candidates*($bar_height+$bar_spacing)+50;

脚本的第2部分创建了一些用来实际绘制图形的变量。

计算出这些类型的变量值是冗长乏味的,但是想想完成之后的图像是什么模样,会使这个过程变得轻松一点。在这里,我们使用的值是通过在纸上拟定设计效果,然后再估计所要求的比例而得到的。

宽度变量$width是背景总宽度。我们也设置了左边缘和右边缘的宽度(分别使用$left_margin和$right_margin变量),每条长方形的厚度和它们之间的间距($bar_height和$bar_spacing),以及字体、字体大小和标签的位置($font、$title_size、$main_size、$small_size和$text_indent)。

在给定这些基本的值后,就可以进行一些基本的计算了。我们希望绘制一条基线,所有的横条都从该线伸展开。通过在左边缘加上文本标签的宽度可以得到基线的x坐标位置,然后根据边框线估计基线的y坐标位置。如果灵活性非常重要,还可以根据最长的名称获得确切的宽度。

我们还需要计算两个重要的值:第一,在图像上表示一个单元的宽度:

$bar_unit=($width-($x+$right_margin))/100;//one/"point/"on the graph

这是长方形条的最大宽度——从基线到右边空白处——分为100份,因为我们的图形要显示百分值。

第二个值是背景总高度:

$height=$num_candidates*($bar_height+$bar_spacing)+50;

这基本上是每一条长方形的高度乘以其数据,再加上标题所需的高度。脚本的第3部分如程序清单22-5-3所示。

程序清单22-5-3 show_poll.php——脚本的第3部分建立图形并准备添加数据

/*******************************************

Set up base image

*******************************************/

//create a blank canvas

$im=imagecreatetruecolor($width,$height);

//Allocate colors

$white=imagecolorallocate($im,255,255,255);

$blue=imagecolorallocate($im,0,64,128);

$black=imagecolorallocate($im,0,0,0);

$pink=imagecolorallocate($im,255,78,243);

$text_color=$black;

$percent_color=$black;

$bg_color=$white;

$line_color=$black;

$bar_color=$blue;

$number_color=$pink;

//Create/"canvas/"to draw on

imagefilledrectangle($im,0,0,$width,$height,$bg_color);

//Draw outline around canvas

imagerectangle($im,0,0,$width-1,$height-1,$line_color);

//Add title

$title=/'Poll Results/';

$title_dimensions=imagettfbbox($title_size,0,$font,$title);

$title_length=$title_dimensions[2]-$title_dimensions[0];

$title_height=abs($title_dimensions[7]-$title_dimensions[1]);

$title_above_line=abs($title_dimensions[7]);

$title_x=($width-$title_length)/2;//center it in x

$title_y=($y-$title_height)/2+$title_above_line;//center in y gap

imagettftext($im,$title_size,0,$title_x,$title_y,

$text_color,$font,$title);

//Draw a base line from a little above first bar location

//to a little below last

imageline($im,$x,$y-5,$x,$height-15,$line_color);

在第3部分,我们将建立基本图像,分配颜色,然后开始绘制图形。

首先,我们将填充图形的背景颜色,使用如下所示函数:

imagefilledrectangle($im,0,0,$width,$height,$bg_color);

顾名思义,ImageFilledRectangle函数将绘制一个中间填充了颜色的矩形。该函数的第一个参数与其他图形函数一样都是图像标识符。然后我们将此矩形的起点和结束点的x与y坐标传递给该函数。其起始点和结束点分别对应矩形的左上角和右下角。在这个例子中,我们还在整个背景中填充了背景颜色,该颜色是最后一个参数:白色。

然后调用:

imagerectangle($im,0,0,$width-1,$height-1,$line_color);

如上语句将绘制出围绕画布边沿的黑色轮廓线。该函数绘制了一个轮廓矩形而不是实心矩形。而其参数与前面的函数是一样的。请注意,我们绘制的矩形坐标是一直绘制到$width-1和$height-1——从(0,0)一直到画布的宽度和高度。如果我们绘制的是$width和$height,矩形将超出画布区域。

在这里,我们使用了前一个示例所介绍的逻辑和函数,将标题写到图像中,并将其置于图像中央。

最后,我们为每个长方形图绘制了基线:

imageline($im,$x,$y-5,$x,$height-15,$line_color);

ImageLine函数可以在我们所指定的图像($im)上画一条线,我们指定了该直线的起点坐标($x,$y-5)和终点坐标($x,$height-15),并通过变量$line_color指定颜色。

在这个例子中,我们在第一条长方形图的上面一点绘制了基线,其终点是背景的底端一点。

现在,我们准备将数据填充到该图形上。脚本的第4部分如程序清单22-5-4所示。

程序清单22-5-4 show_poll.php——脚本的第4部分将实际的数据绘制到该图像上,并结束整个程序

/*******************************************

Draw data into graph

*******************************************/

//Get each line of db data and draw corresponding bars

while($row=$result->fetch_object)

{

if($total_votes>0)

$percent=intval(($row->num_votes/$total_votes)*100);

else

$percent=0;

//display percent for this value

$percent_dimensions=imagettfbbox($main_size,0,$font,$percent./'%/');

$percent_length=$percent_dimensions[2]-$percent_dimensions[0];

imagettftext($im,$main_size,0,$width-$percent_length-$text_indent,

$y+($bar_height/2),$percent_color,$font,$percent./'%/');

//length of bar for this value

$bar_length=$x+($percent*$bar_unit);

//draw bar for this value

imagefilledrectangle($im,$x,$y-2,$bar_length,$y+$bar_height,$bar_color);

//draw title for this value

imagettftext($im,$main_size,0,$text_indent,$y+($bar_height/2),

$text_color,$font,/"$row->candidate/");

//draw outline showing 100%

imagerectangle($im,$bar_length+1,$y-2,

($x+(100*$bar_unit)),$y+$bar_height,$line_color);

//display numbers

imagettftext($im,$small_size,0,$x+(100*$bar_unit)-50,$y+($bar_height/2),

$number_color,$font,$row->num_votes./'//'.$total_votes);

//move down to next bar

$y=$y+($bar_height+$bar_spacing);

}

/*******************************************

Display image

*******************************************/

Header(/'Content-type:image/png/');

imagepng($im);

/*******************************************

Clean up

*******************************************/

imagedestroy($im);

?>

第4部分脚本将在数据库中逐个检索候选人,计算各自投票百分比,并为每位候选人绘制条形图并写上标签。

这里,我们又调用了ImageTTFText函数来为图像添加标签。我们调用ImageFilled-Rectangle函数绘制一个实心条形图:

imagefilledrectangle($im,$x,$y-2,$bar_length,$y+$bar_height,$bar_color);

并使用ImageRectangle函数绘制了标志100%的轮廓:

imagerectangle($im,$bar_length+1,$y-2,

($x+(100*$bar_unit)),$y+$bar_height,$line_color);

在完成所有这些条形图之后,调用ImagePNG函数输出了图像,并调用ImageDestroy函数清理资源。

这是一段比较长的代码,但是它合乎需要,或者适合通过一个界面自动产生投票结果。该脚本一个重要的缺陷是它缺乏反欺骗机制。用户很快就可以发现他们可以重复投票而使投票结果变得毫无意义。

我们可以使用类似的方法绘制直线图形,甚至如果擅长数学的话,还可以用这样的方法来绘制杂乱的流程图。