Облако тэгов на DelphiИсточник: webdelphi
Сегодня рассмотрим интересную тему - разработка облака тэгов на Delphi. Дело в том, что в Интернет полно разного рода php- и js-скриптов по этой теме, а вот до Delphi руки особо у программистов не доходят. Между тем облака тэгов широко используются в программных продуктах, например в блог-клиентах и знание о том, как эти облака строятся будут далеко не лишними. Прежде, чем приступать к разработке, обратимся к wiki что бы уяснить, что из себя представляет облако тэгов:
Итак, первое, что сразу берем на заметку - чем выше релевантность, тем больше изображение тэга. Теперь определим для себя, что мы должны получить на входе и выходе нашего проекта. На вход поступает неструктурированный набор (список) тэгов. Это может быть таблица базы данных или простой список типа TStrings и пр. На выходе мы должны получить список уникальных (не повторяющихся) тэгов, причем, чем чаще встречается тэг в общем списке, тем больше его изображение. Сразу договоримся - на выход будет поступать список отсортированный по мере релевантности тэга. Вообще, обычно на выходе список отсортированный по алфавиту, но т.к. представленный способ лишь показывает возможность составления облака, то сортировки по релевантности нам будет достаточно. Теперь приступаем к разработке нашего мини-приложения. 1. Подготовка к разработке облака тэгов на Delphi Для того, чтобы достигнуть поставленной цели нам необходимо:
Чтобы долго не засиживаться над малозначащими (пока) для нас деталями, будем представлять общий список тэгов в виде списка в TListBox , а для вывода облака - простой RichEdit. Кроме того, необходимо сразу определить минимальный размер шрифта для тэга - оставим это на милость пользователя. При разработке интерфейса будущей программы у меня получилась примерно следующая форма приложения: Главная форма приложения В список слева заносится набор тэгов, в RichEdit (он справа сверху) выводится само облако, а снизу в ListBox выносится список тегов отсортированный в порядке упоминания в списке. Последнее сделано просто для наглядности работы приложения. Кроме того, пользователь сам задает минимальный размер шрифта для облака. 2. Разработка "внутренностей" программы. Итак, первое, что следует зделать - это объявить следующий типы данных: type TCloud = record Tag : string; //тэг Incoming : integer;//количество упоминаний в списке FontSize : integer;//размер шрифта end; Собственно этот тип будет у нас основополагающим во всей программе. Теперь добавим глобальные переменные: var Cloud : array of TCloud; //облако тэгов min_incom: integer; //минимальное число вхождений тэга в список min_font: integer; //минимальный размер шрифта Первая процедура, которая сразу приходит на ум - добавление нового тэга в список. Я её реализовал следующим образом: procedure Add2Cloud(Tag: string); var i:integer; incloud: boolean; //true - если тэг есть в облаке begin for I:=0 to length(Cloud)-1 do begin if Cloud[i].Tag=Tag then begin inc(Cloud[i].Incoming); наращиваем число вхождений тэга на 1, если он найден в облаке incloud:=true; break; end; end; if not incloud then //добавляем новый тэг в облако begin setlength(Cloud, length(Cloud)+1); Cloud[length(Cloud)-1].Tag:=Tag; Cloud[length(Cloud)-1].Incoming:=1; end; end; То есть, берется облако и сканируется на предмет наличия добавляемого тэга. Если тэг найден, то просто наращиваем счетчик его вхождений на 1, иначе вносим новый тэг в облако. Теперь составим процедуру сортировки облака в порядке возрастания релевантности. Для этого я решил использовать пузырьковый метод сортировки + вставки. На мой взгляд этот метод наиболее прост в реализации и достаточно хорошо себя показывает при работе с небольшими массивами данных. Процедура сортировки облака представлена ниже: procedure SortCloud; var i,j: integer; Temp: TCloud; begin {нулевой проход} //проходим от последнего элемента к первому for i:=length(Cloud)-1 downto 1 do begin {нижний элемент меньше ("легче") верхнего - поднимаем его выше} if Cloud[i].Incoming<Cloud[i-1].Incoming then begin Temp:=Cloud[i]; Cloud[i]:=Cloud[i-1]; Cloud[i-1]:=Temp; end; end; //сортируем оставшуюся часть массива i:=length(Cloud)-1; repeat //проход от последнего до j-го элемента сверху for j:=length(Cloud)-1 downto length(Cloud)-i do begin if Cloud[j].Incoming<Cloud[j-1].Incoming then begin Temp:=Cloud[j]; Cloud[j]:=Cloud[j-1]; Cloud[j-1]:=Temp; end; end; i:=i-1; until i<=0; end; Вначале "поднимаем" наверх элемент облака с самой маленькой релевантностью, а затем продвигаемся "снизу - верх" до n-i-го элемента облака сортируя элементы в необходимом нам порядке. Процедура вывода отсортированного облака в ListBox выглядит следующим образом: procedure GetSortCloud; var i:integer; begin SortCloud; //сортируем облако ListBox2.Clear; //стираем предыдущий результат for i:=0 to Length(Cloud)-1 do ListBox2.Items.Add(Cloud[i].Tag+' ('+IntToStr(Cloud[i].Incoming)+')'); end; Результат сортировки облака в работающей программе будет выглядеть вот так: Вначале выписывается тэг, а за ним в скобках - количество упоминаний в общем списке. По сути дела уже сейчас мы получили на 90% работающую программу, .е. определили гланой - пары "тэг - релевантность". Осталось дело за малым - назначить каждому тэгу свой размер шрифта и вывести все облако в RichEdit . Я не стал сильно углубляться в вопросы оптимизации облака и выбора оптимального алгоритма назначения размеров шрифта, т.к. это очень сильно отвлекло бы нас от основной задачи, а сделал просто: самому редко упоминающемуся тэгу назначил минимальный размер шрифта, а остальным - увеличил шрифт на столько пикселей, сколько раз он упоминается в общем списке…звучит может и несколько мудрённо, но реализуется достаточно просто: procedure GetCloudFonts; var i:integer; begin //определяем минимальное вхождений тегов в облако min_incom:=Cloud[0].Incoming; for i:=0 to Length(Cloud)-1 do if Cloud[i].Incoming<min_incom then min_incom:=Cloud[i].Incoming; //назначаем размер шрифта для каждой группы for i:=0 to Length(Cloud)-1 do begin if Cloud[i].Incoming=min_incom then Cloud[i].FontSize:=min_font else Cloud[i].FontSize:=min_font+Cloud[i].incoming end; end; Вот теперь осталось вывести облако пользователю. Чтобы облако выглядело более менее красиво, я назначил каждому тэгу дополнительно случайный цвет: procedure PaintCloud; var i:integer; begin RichEdit1.Clear; for i:=0 to Length(Cloud)-1 do begin RichEdit1.SelAttributes.Size:=Cloud[i].FontSize; Randomize; RichEdit1.SelAttributes.Color:=RGB(random(255),random(255),random(255)); RichEdit1.SetSelText(Cloud[i].Tag+' ('+IntToStr(Cloud[i].Incoming)+')'+', '); end; end; В результате, наше облако тэгов на Delphi будет выглядеть следующим образом: Как видите в облаке представлены все тэги в зависимости от их упоминания в списке, а также, для наглядности, в скобках указано количество вхождений конкретного тэга в список. Конечно, в строгом смысле это не совсем облако, а скорее заготовка для разработки облака тэгов, но для решения нашей задачи этого вполне достаточно. Кстати, облако тэгов в популярном блог клиенте Zoundary Raven выглядит примерно также : Можете немного потренироваться и вывести облако отсортированное по алфавиту, как это показано на рисунке. В принципе особых трудностей с сортировкой строковых значений возникнуть у Вас не должно, раз уж вы решили заинтересоваться облаками тэгов на Delphi . А я тем временем, постараюсь доработать сво ю программу и получить облако согласно определению из wiki, т.е. не просто слова, а гиперссылки на соответствующие статьи. Если получиться - обязательно поделюсь результатами с Вами. |