|
||||||||||||||||||
Основы web-программирования для школьного “сайтостроительства”. Лекция 8.“Живописцы, окуните ваши кисти”Вот мы и добрались до последней лекции нашего курса. Формально ее основной темой является рисование средствами PHP. Но вместе с тем в этой лекции аккумулируется многое из того, чем мы занимались ранее. Ведь даже для такого “легкого” дела, как построение диаграммы успеваемости, требуется строить “простые” (с нашей колокольни) запросы к базе данных с использованием группировки. Учебный план
Диаграмма с визуализацией данных об успеваемости и будет нашей основной задачей, но начнем мы эту лекцию с рассмотрения чисто “рисовальных” учебных примеров, которые, впрочем, могут пригодиться вам и сами по себе. Пример 1. Привет черепашкам!Посмотрите, пожалуйста, на рис. 1 и 2. Узнаете задачу?
Рис. 1
Рис. 2 Эта задачка очень популярна среди поклонников Лого, но она, конечно, не специфична именно для черепашьей графики и может быть реализована различными средствами. Базовая идея заключается в следующем: берем правильный многоугольник и делим все его стороны в некотором отношении. Соединяя полученные точки, получим вложенный правильный многоугольник меньшего радиуса. Проделаем с ним то же самое и повторим этот процесс несколько раз. Получим серию вложенных правильных многоугольников. С математической точки зрения для решения этой задачи надо уметь делать две вещи: · находить координаты вершин правильного N-угольника (чтобы построить самый первый правильный многоугольник); · делить отрезок в заданном отношении (т.е. по данным координатам конца отрезка находить координаты точки, которая делит его в отношении k). (Отметим, что при решении на Лого ничего этого уметь не надо :) Формулы для решения указанных задач совсем простые, поэтому я приведу их без вывода. Координаты вершин правильного многоугольника можно найти из соотношений: x[i] = R * cos(360/N * i) y[i] = R * sin(360/N * i) Здесь N — количество вершин, R — радиус, i = 0..N–1, (x[i], y[i]) — координаты i-й вершины. Координаты точки C, которая делит отрезок [A, B] в отношении k (т.е. |AC| = k * |AB|), вычисляются следующим образом: xc = xa + k*(xa – xb) yc = ya + k*(ya – yb) Алгоритмически схема решения задачи построения серии вложенных многоугольников выглядит следующим образом: 1. Инициализируем массив координатами вершин правильного многоугольника. 2. Рисуем очередной многоугольник. 3. Изменяем все элементы массива — записываем в них координаты точек вложенного многоугольника. 4. Повторяем пункты 2–3 заданное количество раз. Рисование средствами PHPВыше мы разобрались с математической и алгоритмической сторонами вопроса. Теперь займемся техникой программирования. Вопрос первый: как выглядит базовая схема?До сих пор мы имели дело со скриптами на PHP, которые возвращали HTML-код. Но этим возможности PHP не ограничиваются. Скрипт на PHP может возвращать картинку — графический файл в одном из стандартных форматов, которые понимают браузеры. В этом случае в параметре src тега img записывается имя скрипта (браузеру-то все равно, какое расширение имеет параметр src, лишь бы то, что он получил в результате, было правильной картинкой). Пример (который мы чуть ниже реализуем): <img src="demoimg.php"> Скрипту, который возвращает картинку, как и обычному PHP-скрипту, можно передавать параметры. Второй пример, который будет рассмотрен ниже, получает в параметрах размеры изображения: <img src="demoimg.php?width=500&height=200"> Вопрос второй: как браузер понимает формат изображения?Выше я сказал: “в одном из стандартных форматов, которые понимают браузеры”. О каких форматах идет речь, примерно понятно — GIF, PNG, JPEG. Непонятно другое: когда браузер видит тег <img src="demoimg.png">, он понимает, что получит картинку в формате PNG. А как он поймет, в каком формате придет картинка <img src="demoimg.php">? Ответ такой: на самом деле и в случае с <img src="demoimg.png"> браузер не может быть на 100% уверен, что он получит именно картинку в формате PNG. Дело в том, что браузер запрашивает у web-сервера не “картинку в формате PNG”, а просто файл demoimg.png. Ответ же web-сервера состоит из двух частей. В первой короткой части web-сервер отправляет браузеру специальный параметр Сontent-type, в котором указан реальный тип содержимого файла. И только после этого передается собственно содержимое. В случае отправки картинки в формате PNG параметр Content-type имеет вид Content-type: image/png. Именно значение image/png и является определяющим. А как web-сервер понимает, какое значение Content-type отправить? А вот он-то как раз руководствуется расширением файла. Соответствие значений Content-type и расширений файлов записано в настройках web-сервера. В нашем случае (в Denwer) соответствующий файл называется mime.types и находится по адресу /usr/local/apache/conf. Можете ради интереса заглянуть в него и пролистать примерно до 465-й строки. Вот что вы там увидите: … image/gif gif image/ief ief image/jpeg jpeg jpg jpe image/naplps image/png png image/prs.btif image/prs.pti image/svg+xml svg image/t38 image/tiff tiff tif … Если мы самостоятельно создаем картинку, необходимо самим озаботиться тем, чтобы отправить браузеру правильный заголовок Content-type. Ну, все! Все понятно! Как же все это сделать?!В PHP имеется множество функций для работы с изображениями. Мало того: есть три различные библиотеки таких функций: самая старая и простая Exif, самая распространенная, часто используемая и хорошо документированная GD и новая — мощная и удобная объектная ImageMagick. Мы будем использовать GD. Желающие получить полную картину могут заглянуть на страницу официальной документации http://ru2.php.net/manual/ru/refs.utilspec.image.php, на которой описаны все упомянутые средства. Чтобы создать в скрипте PHP чистый холст для рисования, необходимо использовать функцию imagecreate. Она имеет следующий синтаксис: переменная_изображения = imagecreate(ширина, высота); Возвращаемая переменная в дальнейшем будет использоваться во всех графических функциях для работы с данным изображением. Имеются варианты этой функции для создания холста из существующего графического файла. Например, imagecreatefromgif создает холст из файла в формате GIF. Размеры при этом не указываются, т.к. холст создается по размерам имеющейся картинки. Для рисования используется семейство функций вида imagesetpixel, imageline, imagerectangle, imagefilledrectangle и т.д. В каждую из этих функций передаются: · переменная изображения, полученная посредством imagecreate; · параметры фигуры, которую надо нарисовать. Смысл и количество этих параметров зависят от фигуры: например, точка задается двумя координатами, линия и прямоугольник — четырьмя и т.д.; · цвет фигуры. А вот с этого места, пожалуйста, поподробнее… Имеется в виду, про цвет поподробнее. Дело в том, что перед тем, как использовать для рисования некоторый цвет, его необходимо “разместить” в изображении посредством функции imagecolorallocate. Она имеет следующий синтаксис: переменная_цвета=imagecolorallocate(переменная_изображения, R, G, B); Здесь R, G, B — составляющие цвета (от 0 до 255). Для палитровых форматов (GIF и PNG) функция imagecolorallocate имеет побочный эффект — первый заданный цвет автоматически становится цветом фона изображения. Когда изображение отрисовано, его нужно отправить браузеру. Для этого сначала нужно передать правильный заголовок Content-type в зависимости от выбранного формата. Для передачи заголовка используется функция header, которая, вообще говоря, является универсальной и используется не только для работы с изображениями. В нашем же случае ее вызов будет иметь примерно следующий вид: header ("Content-type: image/png"); В этом заголовке сообщается, что дальше будет передано изображение в формате PNG. Чтобы передать холст в формате PNG, есть функция imagepng(переменная_изображения); Соответственно, для передачи холста, например, в формате GIF есть функция imagegif(переменная_изображения); Обратите внимание: мы принимаем решение о формате изображения лишь в момент отправки; пока мы просто рисуем на холсте, нас это не слишком интересует. Наконец, после того как изображение передано, считается хорошим тоном освободить занятую им память посредством вызова функции imagedestroy. Ведь память, отведенная PHP, не безгранична, об этом следует думать даже при web-программировании. Ну, пора посмотреть, как все это работает. Для экспериментов с изображениями я рекомендую создать в Denwer’е отдельный “сайт”, но с этим вы уже освоились — разберитесь сами, как вам удобнее. Немного покощунствуем…В качестве первого мини-примера нарисуем… “Черный квадрат” Казимира Малевича (см. рис. 3). Казимир Малевич Рис. 3 Скрипт blacksquare.php будет рисовать, а файл blacksquare.htm — показывать. В HTM-файл я добавил минимум художественных изысков (в частности, стилевыми средствами создал “рамку” картины). В скрипте тоже присутствует элемент “пижонства”. Я пошел на страницу http://ru.wikipedia.org/wiki/Черный_квадрат и попытался “стащить” с репродукции цвет бумаги оригинала. Получилось, прямо скажу, не очень похоже. Но хоть не белый цвет… Файл blacksquare.htm <html> <head> <meta http-equiv="content-type" content="text/html; charset=windows-1251"> <title>Малевич. Черный квадрат</title> </head> <body> <center> <h1>Черный квадрат</h1> <p><img src="blacksquare.php" width="300" height="300" alt="Черный квадрат" title="Черный квадрат" style="border: 20px ridge gray" > <p>Казимир Малевич<br> Чёрный квадрат, 1915<br> Холст, Масло. 79,5 ? 79,5<br> Государственная Третьяковская галерея, Москва </body> </html> Файл blacksquare.php <?php $image = imagecreate(300, 300); $papercolor = imagecolorallocate($image, 235, 227, 206); $black = imagecolorallocate($image, 0, 0, 0); imagefilledrectangle($image,29,19,279,279,$black); header ("Content-type: image/png"); imagepng($image); imagedestroy($image); ?> Внесем в код скрипта минимальные изменения — научим его понимать параметры. Мы используем два параметра — размер (в пикселях) картины и отношение сторон черного квадрата и картины. (Вот уж это настоящее кощунство!) Теперь в HTM-файле будет стоять вызов скрипта с параметрами: <img src="blacksquare1.php?size=400&k=0.85" width="400" height="400" alt="Черный квадрат" title="Черный квадрат" style="border: 20px ridge gray" > А сам скрипт blacksquare1.php будет таким: <?php $image = imagecreate($size, $size); $papercolor = imagecolorallocate($image, 235, 227, 206); $black = imagecolorallocate($image, 0, 0, 0); imagefilledrectangle($image, ($size-$size*$k)/2, ($size-$size*$k)/2, $size-($size-$size*$k)/2, $size-($size-$size*$k)/2, $black); header ("Content-type: image/png"); imagepng($image); imagedestroy($image); ?> Вы еще не забыли про вложенные многоугольники?Вооруженные новыми знаниями, вернемся к задаче про вложенные многоугольники. Ее решение тоже состоит из двух файлов. Скрипт poly.php рисует семейство вложенных N-угольников. Кроме N, ему передаются параметры R, k и M — количество вложенных многоугольников. В скрипте poly.php используется удобная в данном случае функция imagepolygon. Она за один раз рисует произвольный многоугольник, используя в качестве параметра массив координат. В этом массиве должно быть четное количество элементов, и каждая пара элементов понимается как координата очередной точки. Скрипт demopoly.php выводит форму с параметрами и саму картинку (результат работы этого скрипта как раз и показан на рис. 1, 2). Скрипт poly.php <?php /* Посредством define в PHP задаются константы. Константа — не переменная, это просто символьная строка, которая при интерпретации текста заменяется на указанное значение. Перед именами констант не ставится знак $. По традиции программисты на PHP обычно записывают константы строчными буквами. Но это не обязательно. */ define("WIDTH",$r*2+20); define("HEIGHT",$r*2+20); /* Координаты в изображении отсчитываются от левого верхнего угла. Две следующих функции переводят "нормальные" координаты на плоскости в систему координат изображения. */ function xi($x) { return round($x+WIDTH/2); } function yi($y) { return round(HEIGHT/2-$y); } function recalc(&$ar,$num,$k) { /* Функция пересчитывает координаты вершин очередного вложенного многоугольника. */ $ar0x=$ar[0]; $ar0y=$ar[1]; for ($i=0;$i<$num-1;$i++) { $ar[2*$i]=$ar[2*$i]+$k*($ar[2*($i+1)]-$ar[2*$i]); $ar[2*$i+1]=$ar[2*$i+1]+$k*($ar[2*($i+1)+1]-$ar[2*$i+1]); } $ar[2*($num-1)]=$ar[2*($num-1)]+$k*($ar0x-$ar[2*($num-1)]); $ar[2*($num-1)+1]=$ar[2*($num-1)+1]+$k*($ar0y-$ar[2*($num-1)+1]); } for ($i=0;$i<$n;$i++) { // Начальная инициализация массива координат исходного правильного многоугольника $poly[2*$i]=$r*cos(deg2rad($i*360/$n)); $poly[2*$i+1]=$r*sin(deg2rad($i*360/$n)); } $image = imagecreate(WIDTH, HEIGHT); $white = imagecolorallocate($image, 255, 255, 255); $black = imagecolorallocate($image, 0, 0, 0); imagerectangle($image,0,0,WIDTH-1,HEIGHT-1,$black); for ($i=0;$i<$m;$i++) { // Основной цикл. Сделаем его M раз... for ($j=0;$j<$n;$j++) { //Сформируем массив $polyi для использования функции imagepolygon $polyi[2*$j]=xi($poly[2*$j]); $polyi[2*$j+1]=yi($poly[2*$j+1]); } //Нарисуем очередной многоугольник imagepolygon($image,$polyi,$n,$black); //Пересчитаем координаты recalc($poly,$n,$k); } header ("Content-type: image/png"); imagepng($image); imagedestroy($image); ?> Скрипт demopoly.php
<html> <head> <meta http-equiv="content-type" content="text/html; charset=windows-1251"> <title>Вложенные правильные многоугольники</title> </head> <body> <h1>Вложенные правильные многоугольники</h1> <form action="demopoly.php"> <table border="0"> <tr> <td>Количество сторон:</td> <td><input type="text" size="3" name="n" <?php if (isset($n)) echo "value='$n'" ?> > </td> </tr> <tr> <td>Радиус:</td> <td><input type="text" size="3" name="r" <?php if (isset($r)) echo "value='$r'" ?> > </td> </tr> <tr> <td>Коэффициент:</td> <td><input type="text" size="3" name="k" <?php if (isset($k)) echo "value='$k'" ?> > </td> </tr> <tr> <td>Количество вложенных:</td> <td><input type="text" size="3" name="m" <?php if (isset($m)) echo "value='$m'" ?> > </td> </tr> <tr> <td colspan="2"><input type="submit" value="Построить" name="draw"></td> </table> </form> <?php if (isset($draw)) echo "<img src='poly.php?r=$r&n=$n&k=$k&m=$m' alt='Результат' title='Результат'>"; ?> </body> </html> Пример 2. Привет Excel’ю!Я собираюсь нарисовать диаграмму для визуализации данных об успеваемости из нашего журнала. Прежде всего я немного добавил учеников и оценок, чтобы диаграмма получилась более содержательной. Теперь мой журнал имеет следующий вид:
Рис. 4 А страница, которую я хочу получить, должна быть такой:
Рис. 5 Сначала я приведу текст скрипта для страницы, показанной на рис. 5 (у меня он называется demodiag.php). Скрипт demodiag.php <html> <head> <meta http-equiv="content-type" content="text/html; charset=windows-1251"> <title>Диаграмма успеваемости</title> </head> <body> <h1>Диаграмма успеваемости</h1> <img src="diag.php?width=500&height=200" border="1" alt="Диаграмма" title="Диаграмма" > <p> <table border="1"> <tr> <th>№</th> <th>Фамилия, имя</th> <th>Средний балл</th> <th>Рейтинг в классе</th> </tr> <?php mysql_connect("localhost", "webuser", "123456789"); mysql_select_db("page"); $result=mysql_query("select student.student_id as id, name, surname, avg(grade) as avgg from student, grade where student.student_id=grade.student_id group by student.student_id order by avgg desc"); $rank=1; while ($row=mysql_fetch_assoc($result)) { $id=$row['id']; $name=$row['name']; $surname=$row['surname']; $avgg=$row['avgg']; if (isset($oldavgg) and ($avgg!=$oldavgg)) $rank++; echo "<tr>"; echo "<td>$id</td>"; echo "<td>$surname $name</td>"; echo "<td>$avgg</td>"; echo "<td align='center'>$rank</td>"; echo "</tr>"; $oldavgg=$avgg; } ?> </body> </html> Как видно из текста скрипта, самое сложное в нем — вывод таблички с рейтингом на основе соответствующего запроса (этот запрос — наиболее содержательная часть скрипта). Кроме того, тут есть техническая сложность, которую нужно учесть, — некоторые ученики могут иметь один и тот же рейтинг. Скрипт diag.php <?php $image = imagecreate($width, $height); $white = imagecolorallocate($image, 255, 255, 255); $red = imagecolorallocate($image, 255, 0, 0); $blue = imagecolorallocate($image, 0, 0, 255); mysql_connect("localhost", "webuser", "123456789"); mysql_select_db("page");
$result=mysql_query("select count(*) as cnt from student"); $cnt=mysql_fetch_assoc($result); $w=($width/$cnt['cnt'])-2;
$result=mysql_query("select student_id, avg(grade) as avgg from grade group by student_id order by avgg"); $i=0; while ($row=mysql_fetch_assoc($result)) { $avgg=$row['avgg']; $student_id=$row['student_id']; $h=$avgg*$height/5; imagefilledrectangle($image,$i*($w+2),$height-$h,$i*($w+2)+$w,$height-1,$blue); imagettftext($image,10,0,$i*($w+2)+2,$height-1,$white,'arial.ttf', "$student_id ($avgg)"); $i++; }
$result=mysql_query("select avg(grade) as avgg from grade"); $avg=mysql_fetch_assoc($result); $avgg=$avg['avgg']; imageline($image,0,$height-$avgg*$height/5,$width-1,$height-$avgg*$height/5,$red); imagettftext($image,10,0,5,$height-$avgg*$height/5,$red,'arial.ttf',$avgg); header ("Content-type: image/png"); imagepng($image); imagedestroy($image); ?> В этом скрипте используется вывод текста посредством функции imagettftext. Вообще говоря, с этой функцией связано много чисто технических сложностей (в частности, вывести русские буквы так просто не получится), которые я здесь не буду рассматривать. Желающие узнать больше всегда могут связаться со мной и выяснить все “тонкие вопросы”. Чтобы вывод текста в данном случае заработал в принципе, необходимо найти на компьютере файл arial.ttf (он обычно лежит в каталоге windows/fonts) и скопировать его в тот же каталог, где вы разместите данный скрипт. Смысл параметров imagegetttftext следующий: imagegetttftext(переменная_изображения, кегль, угол поворота надписи, x-координата, y-координата, цвет, имя файла шрифта, строка для вывода). В координатах задается левая нижняя точка надписи. Ну, вот и все. Я не только о лекции, но и о курсе в целом. Надеюсь, вам было интересно. Я буду благодарен за отзывы о данном курсе. Пожалуйста, отправляйте их на адрес so@1september.ru. Надеюсь, что вы продолжите заниматься web-программированием и сможете применить основы знаний, полученных в процессе изучения нашего курса, в практической работе. Кстати, о работе. Вам ведь еще предстоит выполнить итоговую работу! Итоговая работаИтоговую работу необходимо отправить до 28 февраля 2009 г. Уважаемые слушатели! Вам предлагаются два варианта итоговой работы. Первый вариант: использование элементов web-программирования для создания сайта образовательного учреждения. Этот вариант является предпочтительным. Если после изучения нашего курса вы сможете на практике применить освоенные технологии для разработки нового или доработки существующего сайта, курс на 100% достиг поставленной цели. Если вы выберете этот вариант, пожалуйста, пришлите в Педагогический университет следующие материалы: · краткое неформальное (но содержательное!) описание того, для чего и как вы применили технологии web-программирования; · ссылку на сайт в Интернете или образ сайта на электронном носителе (в последнем случае, пожалуйста, приложите все файлы, необходимые для воспроизведения вашего сайта, в частности, SQL-файлы для воспроизведения базы данных, если она используется); · акт о внедрении по форме Педагогического университета, заверенный администрацией вашего образовательного учреждения. Второй вариант: итоговая работа в форме контрольной. Если вы только разрабатываете сайт или думаете об этом и не имеете возможности выбрать первый вариант, пожалуйста, выполните итоговую работу в форме третьей контрольной. Задание этой контрольной работы: доработать (допроектировать) базу данных школьного журнала в предположении, что появляются: · предметы; · классы; · учителя. Кроме доработки самой базы, требуется доработать скрипт визуализации таким образом, чтобы он выводил страницу журнала данного класса по данному предмету (эти данные необходимо передавать в качестве параметров). Требуется доработать только скрипт визуализации, административный интерфейс — по желанию. Если вы выберете этот вариант, пожалуйста, пришлите в Педагогический университет все следующие файлы доработанного проекта, включающие SQL-файл, необходимый для воспроизведения базы данных, и PHP-скрипт(ы) для визуализации. Указание Чтобы получить SQL-файл с образом базы данных, необходимо в phpMyAdmin выбрать вкладку “Экспорт” (см. рис. 6).
Рис. 6 В результате экспорта получается дамп базы данных (см. рис. 7), который можно записать в файл и использовать для воспроизведения. Рис. 7 Се. Ль. Островский | ||||||||||||||||||