Программирование

SVG в Qt

Благодаря бдительным пользователям в qTwister обнаружился баг. Оказывается, для отображения векторных svg-картинок, необходимо в составе приложения поставлять еще и соответствующий плагин, а без него ничего не показывается. В общем, разобрался, все исправил, кто следит — может скачать обновленные архивы со страницы программы (если надо — обновите кэш браузера).

На случай если забуду, или случайный гость заинтересуется: плагин qsvg4.dll (.so для Linux) лежит в qt/plugins/imageformats. В приложении плагин должен поставляться в директории imageformats/.

Notabene — заметки на полях

В общем, сделал я для Друпала 6.x модуль под названием notabene. Модуль предназначается для организации системы кратких заметок «на полях» с помощью специальной кнопки. При наведении мыши на кнопку, появляется всплывающий блок, показывающий уже созданные по данной кнопке заметки. Нажатие на кнопку влечет появление фрейма с уже созданными заметками и формой добавления новых. Фрейм создается с помощью Modal Frame API.

Специальная кнопка может быть вставлена разработчиком сайта куда угодно с помощью функции

  1. theme('notabene_button', $rel_id, $nid = 0);
где $rel_id — задаваемый разработчиком идентификатор, используемый для группировки заметок, а $nid — необязательный ID ноды, к которой будут прикреплены заметки. Вставлять можно сколько угодно кнопок, при разных $rel_id у разных кнопок будут независимые потоки заметок.

То есть по сути модуль представляет собой API из одной theme-функции, возвращающей HTML-код кнопки, и нескольких страниц настроек модуля в админке. Более подробное описание разных аспектов модуля содержится в файле README_RU.txt в архиве. Здесь кратко перечислю основные особенности:

  • если кнопка привязана к ноде через указание $nid, заметки будут автоматически удаляться при удалении ноды;
  • заметки можно помечать как «важные» и «личные». Личные заметки видны только их автору (и админу);
  • можно включить ограничение длины заметок с отображением количества оставшихся символов примерно как в Твиттере;
  • количество заметок, выводимых во всплывающем блоке, и порядок их сортировки тоже можно настраивать;
  • администратор может задать формат ввода, автоматически применяемый ко всем заметкам;
  • есть возможность подключить к форме Друпала (по form_id) автоматическую фильтрацию, с помощью которой модуль найдет в текстовых полях формы куски текста, выделенные определенными тэгами, и сохранит их как заметки, убрав из текстового поля. Тэги настраиваются в админке;
  • администратору доступна система фильтрации и удаления заметок наподобие админки нод;
  • при добавлении/удалении заметок счетчик на кнопке обновляется без перезагрузки страницы.

Также в состав модуля notabene входит подмодуль notabene_node. Он позволяет встраивать кнопку заметок в тело ноды без необходимости использовать функцию theme() и вообще писать какой-либо код. Для этого имеются настройки отображения кнопки как для типов содержимого, так и для каждой ноды в отдельности. Пример настроек и результат отображения кнопки в ноде — на рисунках ниже:

Это первая версия модуля, естественно, еще достаточно сырая, многое можно улучшить, что-то переделать. Делалось, как водится, для собственных нужд, но с оглядкой на реюзабельность. Поэтому если есть идеи, ценные советы или замечания по работе модуля и его апгрейду — прошу высказываться. А пока в дальнейших планах:

  • некоторая интеграция с Views;
  • интеграция с Beauty Tips и, возможно, с Simplecluetip;
  • проработка разных вариантов отображения кнопки;
  • улучшение механизма автофильтрации.

Немного погодя добавлю ролик с демонстрацией и кратким объяснением, зачем все это было нужно (мне).

Скачать модуль: notabene.tar.gz v. 1.0.

qPassGen — генератор паролей

Время от времени всем нам приходится регистрировать учетные записи, для которых неплохо бы иметь «сильный» пароль. Для доступа на ФТП, для админки на сайте, в общем — известно, зачем. Лично я для этих целей обычно ставлю пароли вида NCpg*fmO_08uLh. Придумывать такие самому, однако, лениво. Поэтому уже несколько лет пользуюсь самопальной утилиткой на Qt, которая придумывает их за меня. Ну и пришло как-то в голову привести ее в божеский вид и выложить сюда — а ну как кому пригодится. Знакомьтесь, qPassGen:

Программа генерирует пароли заданной длины из букв латинского алфавита нижнего и верхнего регистра, пробелов, подчеркиваний, цифр и звездочек, а также дополнительных, введенных от руки символов. Краткое описание прилагается в архивах.

qTwister

Как-то раз жена притащила домой заморскую игру Твистер. Если кто не знает, это такая напольная потеха, в которой игроки должны по команде ставить руки и ноги на разноцветные круги, извиваясь как ужи, и кто гибше гнется — тот молодец. Игра была немедленно испытана на гостях, специально приглашенных под надуманным предлогом (см. спиртное). Радость стояла неописуемая — пьяные гости (и мы с ними) яростно изгибались и дружно валились в кучу. Поутру не привыкшие еще к позициям из цикла «Йога для самых гуттаперчевых» конечности адски болели, но сейчас не об этом.

В процессе игры оказалось, что один из присутствующих вынужден воздержаться от всеобщего твистера и быть ведущим, то есть крутить пластмассовую стрелку на диске и называть выпадающие ходы (цвета и конечности). И это в XXI веке, когда очевидно, что в комплект с игрой должны входить специально обученные роботы, самостоятельно крутящие стрелку, пока человеки полным составом валяются по полу.

А я как раз тогда дочитывал книжку Макса Шлее про Qt, ну и решил устранить технологическую несправедливость и заодно закрепить некоторые аспекты фреймворка на практике. Так родилась программка qTwister.

Программа умеет:

  • генерировать ходы для Твистера в ручном режиме (нажатием на красную кнопку);
  • генерировать ходы автоматически с заданным интервалом;
  • отображать каждый ход графически (изображением конечности и цвета, как на картинке выше);
  • озвучивать каждый ход мужским или женским голосом (русскую озвучку сами наговаривали, английская — сгенерирована через espeak);
  • работать на русском и английском языках.

В общем, делюсь разработкой с общественностью. Краткое руководство по использованию прилагается в архиве.

Всякие deb и rpm-пакетики наверное тоже вскоре сделаю. Заодно научусь, как.

Если есть какие вопросы, сообщения, уточнения или пожелания по поводу — пишите в комментарии или в почту.

А если вам хочется поставить генератор ходов для Твистера на мобильный телефон, воспользуйтесь программой Twisdial. Она умеет работать в телефонах на базе Android и других телефонах с поддержкой J2ME.

Ограничение количества символов в textarea

Предположим, нам захотелось ограничить длину текста, вводимого пользователем в текстовое поле нашей формы (да-да, примерно как в Твиттере). «Из коробки» Друпал такой возможности нам не предоставляет, свойства элемента формы, задающего максимальную длину текста в textarea — нет. Однако сделать ограничение руками оказывается довольно просто.

Для определенности озвучим задачу: мы хотим, чтобы для текстового поля note длина текста была ограничена заданным числом. При этом пользователю при вводе выводилось бы оставшееся число символов, а текст, введенный сверх этого числа — беспощадно удалялся (для чего задействуем jQuery).

Qt, Symbian и Linux

Блог лабораторий Trolltech, разработчиков Qt, разродился хорошей новостью. В грядущем Qt 4.7 станет возможной разработка и отладка софта для Symbian прямо из-под Линукса. Возможность пока экспериментальная, тролльтехи ее планируют улучшать, упрощать и дорабатывать, но серьезный шаг вперед уже сделан.

В заметке «Symbian development using Linux» Томас Зандер рассказывает о том, как и что нужно устанавливать, чтобы начать плодотворную разработку уже сейчас.

Рукотворные категории профиля в Panels

Добавлял я вчера с помощью модуля новую категорию в профиль пользователя. Через hook_user(). Выглядит примерно так:

  1. function mymodule_user($op, &$edit, &$account, $category = NULL) {
  2.   switch ($op) {
  3.     case 'categories' :
  4.       $categories = array();
  5.       $categories[] = array('name' => 'new_category',
  6.                             'title' => t('New category'),
  7.                             'weight' => 10,);
  8.       return $categories;
  9.     case 'load' :
  10.       $account->field_1 = 1;
  11.       $account->field_2 = 2;
  12.       $account->field_3 = 3;
  13.       break;
  14.     case 'view' :
  15.       $account->content['new_category'] = array(
  16.         '#type' => 'user_profile_category',
  17.         '#title' => t('New category'),
  18.       );
  19.       $account->content['new_category']['field_1'] = array(
  20.         '#type' => 'user_profile_item',
  21.         '#title' => t('Field 1'),
  22.         '#value' => $account->field_1,
  23.       );
  24.       $account->content['new_category']['field_2'] = array(
  25.         '#type' => 'user_profile_item',
  26.         '#title' => t('Field 2),
  27.        '#value' => $account->field_2,
  28.       );
  29.       $account->content['new_category']['field_3'] = array(
  30.         '#type' => 'user_profile_item',
  31.         '#title' => t('Field 3'),
  32.         '#value' => $account->field_3,
  33.       );
  34.       break;
  35.   }
  36. } //function mymodule_user

Ну то есть добавляю три поля и они немедленно появляются в профиле каждого пользователя. Но вот незадача — популярный модуль Panels, который хорош в том числе и для создания красивых профилей пользователей, не умеет автоматически находить рукотворные категории пользователей и засасывать их в панели. Даже родную категорию 'History' (которая в коде называется 'summary') не хочет ни в какую показывать в списке доступных категорий.

В общем думал я, думал. Искал в интернетах и не нашел волшебного модуля, подсказывающего Panels список запрограммированных категорий. Создал тему на Друпал.ру — тоже пока нет результата. Единственное решение, которое я смог придумать — это кастом-панель, в которую выводится результат php-кода, формирующего категорию:

  1. $uid = arg(1);
  2. $user = user_load($uid);
  3. $content = user_build_content($user);
  4. foreach ($content['new_category'] as $item) {
  5.   if ($item['#type'] == 'user_profile_item') {
  6.     print theme('user_profile_item',$item);
  7.   }
  8. }

На том пока и остановился.

Click-sort в табличных Views по виртуальному полю

Недавно писал о том, как подружить свои таблицы данных с модулем Views. Кроме всего прочего, в той заметке рассматривал создание виртуального поля percentage, вычисляемого из двух существующих полей, а также фильтрацию и сортировку выборки по этому виртуальному полю.

А сегодня столкнулся с тем, что сортировка выборки с помощью views_handler_sort_formula не работает в табличных представлениях. Оказывается, когда мы выводим вьюху в виде таблицы и хотим сортировать таблицу щелчком мыши на заголовок одного из столбцов (что очень удобно) — модуль Views не применяет sort-хэндлер, который был указан при создании поля.

Немного покопавшись в документации, выяснил, что почему-то механизм «click-sort», используемый для сортировки табличных представлений, реализован вообще не в sort-хэндлерах, а вовсе и в field-хэндлерах. Именно поэтому сортировка таблицы по столбцу percentage была невозможна: Views пытался добавить в запрос несуществующее поле вместо формулы. Не вдаваясь в причины именно такой реализации, исправим недочет по горячим следам.

Сортировкой таблицы управляет метод views_handler_field::click_sort($order), где $order — порядок сортировки (ASC/DESC, ага). Поскольку мы уже сделали для своего поля хэндлер mytest_handler_field_percentage, нужно просто добавить к этому классу переопределение метода click_sort():

  1. function click_sort($order) {
  2.   $this->query->add_orderby(NULL, NULL, $order, 'ROUND(testnode.int_count*100/testnode.int_total)');
  3. }

Собственно, вот и все. Теперь поле percentage может быть полноценным членом табличного представления.

Как подружить свой тип данных с Views

При разработке модулей для Drupal мы частенько создаем программно новые типы материалов (то есть нод). В процессе иногда возникает соблазн быстренько запрограммировать вывод какой-нибудь отсортированной коллекции этих материалов. Например, вывод нод-городов, отсортированных по алфавиту, или по древности, или сгруппированных по странам. Таких полезных коллекций может образоваться достаточно много и программно создавать каждую из них — не очень эффективно. Особенно если впоследствии нам захочется что-то поменять, объединить, или переделать. Гораздо аккуратнее — использовать модуль Views.

Вообще говоря, Views — один из краеугольных камней разработки на Drupal. Хотите помесячный архив нод? Views. Хотите вывести только заголовки и даты нод? Views. Хотите вывести ноды, сгруппированные по именам авторов? Views. Хотите получить список IP, с которых пользователь Василий заходил на сайт в течение Великого поста? Тоже Views. Один из самых популярных ответов на вопрос «Как в Друпале сделать…» — Views. Так что, чем быстрее разработчик подружится с этим прекрасным модулем — тем лучше.

Однако как только разработчик захочет получить с помощью Views коллекцию созданных им данных — например, из самодельной таблицы, сделанной для нового типа материала — он обнаружит, что Views еще не знает о его таблице. Так вот, раз и навсегда рассказать о своей таблице модулю Views — на мой взгляд, гораздо более правильное решение, чем программировать вручную все необходимые представления.

О том, как это сделать — под катом.

Qt и звуки

Меня всегда радовало разнообразие возможностей по работе со звуком в Линуксе.

Сегодня, например, несколько часов подряд взрывал себе мозг на тему QSound в Qt. Дело в том, что под Виндой, чтобы воспроизвести звуковой эффект без извращений, нужно просто вызвать статический метод QSound::play("имя файла"). В Линуксе тоже так можно, но чтобы звук действительно прозвучал — надо ставить NAS. Узнав об этой необходимости, принялся ставить оный, параллельно размышляя, как лучше заставить пользователей проделать то же самое. Мозги начали нагреваться.

Когда с nasd возникли какие-то проблемы, быстренько переключился на поиск альтернатив. Уже задумался о компиляции версии под Линукс с Phonon. Остановил тот факт, что фононовский учебный проект из состава Qt SDK не собрался out of the box. Попробовал учебный проект QAudioOutput оттуда же — не работает.

Уже начал было впадать в уныние, но в конце-концов осенило:

  1. #ifdef Q_WS_X11
  2.   QProcess::startDetached("play mysound.wav");
  3. #else
  4.   QSound::play("mysound.wav");
  5. #endif

Вроде бы, ерундовая задача, а — чуть стол головой не разбил. Так жить нельзя.