вторник, 3 мая 2016 г.

Nautilus Extension for FictionBook2 (FB2)

Расширение для файлового менеджера Nautilus, показывающее в отдельных колонках (табличный режим просмотра) информацию об авторе книги и её названии.

Исходники можно получить https://github.com/yastrov/nautilus_extension_fictionbook2 , а сборка практически тривиальна и описана в README. (Хотя для версии Nautilus отличной от 3, придётся поправить в Makefile пути к расширениям.)

Наконец-то нашёл время, чтобы осуществить старое желание написать полезное расширение.

В процессе создания пришлось погулять по интернету, причём улов был очень скромный. Очень мало примеров, очень скудная документация, особенно когда дело доходит до не совсем классических вещей.
Поэтому результат представляет собой попытку осмыслить в том числе редкие примеры на языке Си, а так же Python. На последнем примеров больше, хотя мне не кажется скриптование подобных вещей, постоянно находящихся в памяти и работающих, хорошим и оптимальным решением.

Не буду останавливаться на описании интерфейсов самого Nautilus. Тут много рутины, и выглядит оно не самым наглядным на мой взгляд образом. (И, что греха таить - было частично заимствовано из примеров. Но не суть.)

На чём стоит остановиться:

Почему не Python?
Причиной, что кроме любви к Си, было желание сделать компактное (в плане потребляемой памяти) и достаточно быстрое решение. А в качестве бонуса - не смотря на отсутствие бинарников на данный момент, ещё и более простое распространение - установку.

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

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

Долго думал, использовать для отложенного вызова g_timeout_add с секундной задержкой (что популярно, но всё-таки не совсем правильно: может произойти вызов примерно одновременно для множества файлов) или g_idle_add, обещающую вызов тогда, когда снизится нагрузка (про последнюю не вполне понятно, гарантируется ли её вызов в обозримом будущем, или может отложиться на неопределённое время.) Пока победила g_idle_add.

Приведу небольшой фрагмент кода в качестве иллюстрации:

Для того, чтобы можно было использовать асинхронную обработку, необходимо определить примерно следующую структуру:

typedef struct {
    GClosure *update_complete;
    NautilusInfoProvider *provider;
    NautilusFileInfo *file;
    int operation_handle;
    gboolean cancelled;
} UpdateHandle;

А наш callback будет выглядеть:

gint
timeout_plain_fb2_callback(gpointer data);

Посмотрим же основную функцию, которую будет вызывать Nautilus:

static NautilusOperationResult
fb2_extension_update_file_info (NautilusInfoProvider *provider,
                NautilusFileInfo *file,
                GClosure *update_complete,
                NautilusOperationHandle **handle)
{
    if(nautilus_file_info_is_directory(file))
        return NAUTILUS_OPERATION_COMPLETE;
    ...
 
    if (!data) {
        /* Получаем имя файла именно таким способом. */
        char *filename = nautilus_file_info_get_name(file);
        const int len = strlen(filename);
        if(len > 4 && g_strcmp0(&filename[len-4], ".fb2") == 0) {
            /* Заполним структуру, необходимую для асинхронной работы. */
            UpdateHandle *update_handle = g_new0 (UpdateHandle, 1);
            update_handle->update_complete = g_closure_ref(update_complete);
            update_handle->provider = provider;
            update_handle->file = g_object_ref (file);
            // Или так: g_timeout_add (1,
            /* Но лучше так: */
            g_idle_add(
                timeout_plain_fb2_callback,
                update_handle);
            /* Польза данной операции не вполне очевидна... */
            *handle = update_handle;
            g_free(filename);
            /* Сообщаем, что выполнение операции отложено. */
            return NAUTILUS_OPERATION_IN_PROGRESS;    
        } else {
           ...
            }
        }
        g_free(filename);
        return NAUTILUS_OPERATION_COMPLETE;
    }

    return NAUTILUS_OPERATION_COMPLETE;
}

callback Практически из официального примера бородатого года:
В нём мы с помощью g_object_set_data_full кэшируем данные на будущее, и устанавливаем
с помощью nautilus_file_info_add_string_attribute для отображения сейчас:

gint
timeout_plain_fb2_callback(gpointer data)
{
    UpdateHandle *handle = (UpdateHandle*)data;
    /* Не вполне очевидно, что же может отменить операцию, но раз в доках предлагается, оставим. */
    if (!handle->cancelled) {
        char *filename = g_file_get_path(nautilus_file_info_get_location(handle->file));
        ...
        }
        g_free(filename);
    }
 
    nautilus_info_provider_update_complete_invoke
                        (handle->update_complete,
                         handle->provider,
                         (NautilusOperationHandle*)handle,
                         NAUTILUS_OPERATION_COMPLETE);
    /* С handle мы закончили. */
    g_closure_unref (handle->update_complete);
    g_object_unref (handle->file);
    g_free (handle);
    return 0;
}

Вывод:

Если не считать громоздких и не совсем наглядных (особенно когда есть желание сделать один единственный файл - исходник) объявлений интерфейсов самого Nautilus, и бедной документации, в целом процесс легче, чем может показаться, а возможности по кастомизации - большие.

Так же стоит при написании расширения искать исходники и примеры на других языках программирования.

Подборка ссылок:

http://web.archive.org/web/20090418175132/http://www.campd.org/stuff/docs/extending-nautilus/NautilusExtensions.html Официальный Nautilus Extension manual, теперь доступный только из архива:

https://developer.gnome.org/libnautilus-extension/stable/ Nautilus API

https://developer.gnome.org/glib/stable/ - GLib

четверг, 17 марта 2016 г.

Распространение приложений на Qt, или как найти нужные DLL - windeployqt.exe

Проблема распространения приложения на Qt пользователю - больная тема для большинства разработчиков.
Оказывается, она упрощается с помощью утилиты от самих разработчиков Qt. Но к сожалению эта утилита упоминается очень редко, так что я сам узнал о ней только что. Возможно у неё есть недостатки, пока мне неизвестные но на первый взгляд она удобная, если бы не была консольной. Но с последней проблемой мы можем справиться).

Откроем Qt Creator, зайдём в меню "Инструменты"- подпункт "Параметры", слева пункт "Среда", вкладка "Внешние утилиты".
И в нижнем меню "Дабавить" выберем вначале "Новый раздел" (я выбрал ёмко и просто - ) "Добавить утилиту" (соответственно "windeployqt")
И справа заполним следующим образом:

Описание: "Эта утилита добавляет необходимые DLL в папку с EXE"
Программа: %{CurrentProject:QT_INSTALL_BINS}\windeployqt.exe
Параметры: --compiler-runtime %{CurrentProject:BuildPath}\%{CurrentBuild:Type}
Среда: PATH=C:\Qt\Qt5.6.0\Tools\mingw492_32\bin;${PATH}

*В параметрах нужно указать строку, где создаётся наш EXE.) А поскольку редактировать настройки каждый раз неудобно, соберём путь из заботливо предоставленных авторами Qt переменных. У меня расположение компилятора из системной переменной не подхватилось, на всякий случай добавил.*

В результате должно получиться

Можно добавить параметры, отвечающие за копирование плагинов, платформ и т.д. Подробнее:
http://doc.qt.io/qt-5/windows-deployment.html

Так же придётся добавить в системную переменную следующие значения (пример для Qt 5.6.0):
C:\Qt\Qt5.6.0\Tools\mingw492_32\bin
C:\Qt\Qt5.6.0\5.6\mingw49_32\bin
*Адрес компилятора нужен, поскольку DLL из его поставки нам тоже понадобятся.*
*Чтобы изменить системные переменные, можно в диалоге "Выполнить" ОС Windiws запустить sysdm.cpl .*
*Так же можно поэкспериментировать с переменными среды самого Qt Creator.*

Всё, можно собирать проект, затем вызывать нашу утилиту и смотреть каталог! Для удаление мусора из каталога - пункт "Очистить" из меню "Сборка"..
Не забудьте добавить те библиотеки, которые вы импортировали сами!

Qt Auto Resize Widgets

У многих начинающих разработчиков, использующих Qt Creator (а кому время, потраченное на создание интерфейса лишнее?) возникает проблема, как сделать, чтобы виджеты сами подстраивались под размер окна (Auto Resize Widgets).
И никакие настройки элементов не помогают.
На самом деле решение элементарно.

В режиме дизайнера мы можем это легко исправить.
Достаточно выбрать в "инспекторе объектов" наше главное окно (обычно называется centralWidget типа QWidget). И присмотреться в верхней части окна Designer-а (сразу под привычным нам меню). Там мы видим маленькие значки очень похожие на пиктограммы компоновщиков (если навести на них мышкой, то и подсказки будут соответствующими). Достаточно выбрать один, как наше главное поле превратится в компоновщик!

Забавно, но это единственный известный мне способ, хотя логичнее было бы увидеть соответствующий пункт меню в "инспекторе объектов". *Именно из-за этого я считал, что эти кнопки относятся к обычным компоновщикам.)

четверг, 27 марта 2014 г.

Подсказка для работы с консолью:
По умолчанию консоль Windows работает с папкой текущего пользователя системы. Чтобы при каждом запуске консоли не производить смену этой директории, следует для необходимой папки вызвать контекстное меню (предварительно зажав Shift) и выбрать команду "Open command window here".

среда, 26 марта 2014 г.

Parallel keynotes.

Нет блокировкам. Да - future (Py, Java), очередям (Quenue) и, в случае Python-а yield from.

Reactor - конкурентное программирование, цикл, для ОС - один поток. В Stackless и Python 3.4 интегрирован. И такой путь - лучший.

http://www.youtube.com/watch?v=2mfQJaddkNs

Библиотеки:

Gevent
asyncio (бывший tultip)

Работа с потоками и GIL распространённая проблема, отсутствующая в Си, С++ и, может быть, Java.

Так же цитаты хорошего человека:
http://alenacpp.blogspot.ru/2013/11/blog-post.html
Итак, Рихтер рекомендует. (Никаких откровений, впрочем)
Не надо плодить потоки, потому что переключение контекстов - вещь дорогая. Разумно делать столько потоков, сколько в машине есть ядер.
Если уж поток создали, постарайтесь его не блокировать без крайней необходимости. Речь идет не только о мьютексах, но и о Join, Sleep.
 
Не надо играть с thread pool. "Leave the thread pool alone".

Ну а также volatile в C# и в C++ разные. В C# volatile "cannot cache and fence", а в C++ только "cannot cache".

пятница, 22 марта 2013 г.

Matplotlib & кириллица

Сложно сказать, что было в древних версиях matplotlib, но последняя в репозитории Ubuntu 12.04 вполне замечательно дружит с кириллицей, если надписи передаются к строки юникод.(u"")