Hatred's Log Place

DON'T PANIC!

Dec 25, 2010 - 2 minute read - programming c++

Astxx: action для команды QueuePause

Парой слов, что такое Astxx небольшая библиотечка для работы с Asterisk через AMI протокол из C++. Написана с использование STL и Boost.

В одной из прошлых заметок упоминал про дружбу Qt4 и Boost.Signal - для дружбы этой библиотечки и Qt4 это и было нужно :)

Для чего нужна команда, например для организации софтовой DnD кнопки: если интерфейс будет в паузе, из очереди на него не будут переводиться звонки.

Сами классы подкатом.

UPD Переоформил в стиле Astxx

/* 
 * Additonal actions for Astxx
 * Copyright (C) 2010  Alexander Drozdov <hatred@inbox.ru>
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/** @file
 *
 * Include this file to use the QueuePause action. You should include it by hands.
 *
 */

#ifndef QUEUE_PAUSE_H
#define QUEUE_PAUSE_H

#include <astxx/manager/basic_action.h>
#include <astxx/manager/message.h>
#include <astxx/manager/error.h>

#include <vector>
#include <string>

namespace astxx
{
    namespace manager
    {                                                                                                                                                                                                                                        
        namespace action                                                                                                                                                                                                                     
        {                                                                                                                                                                                                                                    
            /** Set Queue Pause state                                                                                                                                                                                                        
            */                                                                                                                                                                                                                               
            class queue_pause : public basic_action                                                                                                                                                                                          
            {                                                                                                                                                                                                                                
            public:                                                                                                                                                                                                                          
                class error : public manager::error                                                                                                                                                                                          
                {                                                                                                                                                                                                                            
                public:                                                                                                                                                                                                                      
                    explicit error(const std::string& desc) throw() : manager::error(desc) { }                                                                                                                                               
                };                                                                                                                                                                                                                           
                                                                                                                                                                                                                                             
            public:                                                                                                                                                                                                                          
                /** Construct a QueuePause action.
                * @param interface      - member
                * @param is_on          - pause state
                * @param queue          - queue for process, if omited - all queues is used
                */
                queue_pause(const std::string &interface, const bool is_on = false, const std::string &queue = "") :
                    _interface(interface),
                    _queue(queue),
                    _is_on(is_on) {}

               /** Format this action as a message::action.
                * @return this message as a message::action
                */
                message::action action() const
                {
                    message::action action("QueuePause");

                    if (!_queue.empty())
                    {
                        action["Queue"] = _queue;
                    }

                    action["Interface"] = _interface;

                    std::string on_off_str = "false";
                    if (_is_on)
                    {
                        on_off_str = "true";
                    }

                    action["Paused"] = on_off_str;

                    return action;
                }

                message::response handle_response(message::response response)
                {
                    basic_action::handle_response(response);
                    if ("Success" != response)
                    {
                        throw queue_pause::error(response["Message"]);
                    }

                    return response;
                }

            private:
                std::string _interface;
                std::string _queue;
                bool        _is_on;
            };

        }
    }
}

#endif // QUEUE_PAUSE_H

Dec 25, 2010 - 1 minute read - programming c++

Qt4: поместить окно на все рабочие столы

Встроенного функционала нет, для X11 можно записпользовать такой код (юзается XLib):


#ifdef Q_WS_X11
#  include <X11/Xlib.h>
#  include <X11/Xatom.h>
#endif

...

toAllDesktops()
{
#ifdef Q_WS_X11
    Atom atom = XInternAtom(x11Info().display(), "_NET_WM_DESKTOP", True);
    if (atom)
    {
        uint32_t data = 0xFFFFFFFF;
        XChangeProperty(x11Info().display(),                // Display
                        winId(),                            // Window
                        atom,                               // Property
                        XA_CARDINAL,                        // Property type
                        32,                                 // Data format: 8, 16 or 32 bit
                        PropModeReplace,                    // Property change mode
                        reinterpret_cast<uint8_t*>(&data),  // Property data
                        1);                                 // Data elements count
    }
#endif
}

тут предполагается, что эта функцейка будет медотом класса окна, иначе нужно будет передавать Display и Window. Специальное значение 0xFFFFFFFF как раз и говорит - разместить на всех рабочих столах, иначе воспринимается как номер стола, куда нужно поместить окно. В случае Windows этот функционал бесмесленен, поэтому портабельность не сильно пострадает, при использовании директив препроцессора.

Подробности:

Dec 13, 2010 - 1 minute read - programming c++

Boost.Signals и Qt4

Использую библиотечку astxx, которая использует Boost.Signals, использую в программе на Qt, сразу словил ошибку компиляции:

g++ -c -m64 -pipe -g -Wall -W -D_REENTRANT -DQT_CORE_LIB -DQT_SHARED -I/usr/share/qt/mkspecs/linux-g++-64 -I../queue-test -I/usr/include/QtCore -I/usr/include -I. -I../queue-test -I. -o main.o ../queue-test/main.cpp
In file included from /usr/include/boost/signals/connection.hpp:13:0,
                 from /usr/include/boost/signals/signal_template.hpp:18,
                 from /usr/include/boost/signals/signal0.hpp:24,
                 from /usr/include/boost/signal.hpp:19,
                 from /usr/include/astxx/manager/connection.h:40,
                 from /usr/include/astxx/manager.h:32,
                 from ../queue-test/main.cpp:8:
/usr/include/boost/signals/detail/signals_common.hpp:26:13: error: expected identifier before ‘protected’
/usr/include/boost/signals/detail/signals_common.hpp:26:13: error: expected unqualified-id before ‘protected’
../queue-test/main.cpp:75:1: error: expected ‘}’ at end of input
make: *** [main.o] Error 1

Описания способов решения нашел тут:

http://www.boost.org/doc/libs/1_45_0/doc/html/signals/s04.html

Пока же, временно, определил макрос:

#define BOOST_SIGNALS_NAMESPACE boost_signals

Upd:

В .pro файле добавил:

CONFIG += no_keywords

После чего такие конструкции перестанут компилироваться:

signals:
private slots:
public slots:

Заменить на:

Q_SIGNALS:
private Q_SLOTS:
public Q_SLOTS:

Dec 8, 2010 - 2 minute read -

Немного разного про Git

Несколько вопросов про Git внутри компании, такие как:

  • миграция с mercurial (hg)
  • организация совместного доступа, управление правами
  • прикручивание к redmine
  • организация анонимного ReadOnly доступа к отдельным репозиториям

Миграция с Mercurial

Тут не буду вдаваться в особые подробности, воспользовался этой статьёй: http://hedonismbot.wordpress.com/2008/10/16/hg-fast-export-convert-mercurial-repositories-to-git-repositories/, отработало, историю правок сохранила. Единственно, на ArchLinux пришлось пробежаться и заменить вызовы python на python2.

Организация совместного доступа

Вот тут интереснее, можно это делать на основе пользователей и групп в системе, но мне захотелось что бы был один аккаунт и там уже разруливалось кому что и где можно.

Нашел статью: http://scie.nti.st/2007/11/14/hosting-git-repositories-the-easy-and-secure-way. Где собственно описывается решение на основе gitosis. Можно на просторах рунета найти перевод этой статьи на русский.

Из всего, что хотелось бы добавить: пользователей нужно в конфиге указывать как USER@HOST, иначе получите великий отлуп. Я на этом обжёгся. Решение мне не очень нравится, поэтому попробую раскурить как обойтись просто указанием USER.

Ну и как справка, сгенерировать ключик: ssh-keygen -f .ssh/$USER@$HOSTNAME

pub-часть отдать админу, что бы залил (кстати, так же через git) и прописал права. Private часть прописать для хоста с git, как - man ssh_config

Прикручивание к redmine

По умолчанию (у нас сервер с Debian) http сервер запущен с указанием группы www-data, тогда как репозитории git доступны на чтение/запись пользователю git и на чтение группе git. Вариант - добавить пользователя www-data в группу git, но не очень гибко. Я сделал следующим образом.

В корень домашней директории (/home/git) поместил следующий скрипт:

#!/bin/bash

cd /home/git
source .web-access.conf
cd repositories

# 1. pass
chown $git_user:$git_group *

# 2. pass
for repo in $repos
do
  chown $git_user:$web_group $repo
done

В /etc/sudoers добавил следующую строчку: git ALL=NOPASSWD: /home/git/allow-web-access.sh

В хук /home/git/repositories/gitosis-admin.git/hooks/post-update добавил следующую строчку: sudo -u root /home/git/allow-web-access.sh

Сделал симлинк: ln -s /home/git/repositories/gitosis-admin.git/gitosis-export/web-access.conf /home/git/.web-access.conf

Создал в рабочей копии конфигурации гитозиса файл:

repos="repo1.git repo2.git"

git_user="git"
git_group="git"
web_group="www-data"

в repos указываются репозитории к которым разрешён доступ web-серверу. После чего, как обычно: git commit -a git push

Все нужные действия выполнятся автоматом.

В самом redmine указываем полные пути к репозиториям, типа: /home/git/repositories/repo1.git

Анонимный ReadOnly доступ к некоторым репозиториям

В статье про гитозис про это есть, там правда использована опция --export-all, убираем её. Внутри директории репы, к которой нужен анонимный доступ, делаем файл git-daemon-export-ok: touch git-daemon-export-ok

На будущее - автоматизировать эту операцию через конфигурацию гитозиса.

Dec 2, 2010 - 2 minute read - programming

Java Enums

Для тех случаев когда enum нету, к примеру, при использовании BeanShell:

http://alumnus.caltech.edu/~leif/OO/Enum.html

Подкатом небольшое дополнение и пример.

Небольшая ремарка, для BeanShell придется разносить класс и сами создания констант, к примеру так:

// Не обязательно
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 Хранит описание одной записи в дополнительных действиях, а так же собирает их в перечисление
 дабы можно было использовать интерфейс коллекций для перебора при формировании списка
*/
class AdditionalActionId
{
    private static Collection all_ids = new ArrayList();

    private int    value;
    private String description;

    /**
     Конструктор, принимает ID дополнительного действия и описание (что будет в меню отображаться)
    */
    AdditionalActionId(int value, String description)
    {
        this.value       = value;
        this.description = description;

        all_ids.add(this);
    }

    /**
     Возвращает Id действия
    */
    public int toInt()
    {
        return this.value;
    }

    /**
     Возвращает описание действия
    */
    public String toString()
    {
        return this.description;
    }

    /**********************************************************************************************/
    /* Statics methods (class wide methods) ------------------------------------------------------*/
    /**********************************************************************************************/

    /**
     Получаем итератор для перебора всех ранее созданных действий
    */
    public static Iterator getIterator()
    {
        return all_ids.iterator();
    }

    /**
     Получаем количество дополнительных действий
    */
    public static int getSize()
    {
        return all_ids.size();
    }
}

/**
 Класс-обертка, для реализации некоторого подобия Enum (который не работает в BeanShell)
*/
class AdditionalActionIds
{
    private static int action = 1;

    // Перечисляем наши действия
    //public static final AdditionalActionId TARIFF_ADD_COMMON1 = new AdditionalActionId(action++, "TEST");
    //public static final AdditionalActionId TARIFF_ADD_COMMON2 = new AdditionalActionId(action++, "TEST");
    //public static final AdditionalActionId TARIFF_ADD_COMMON3 = new AdditionalActionId(action++, "TEST");
    //public static final AdditionalActionId TARIFF_ADD_COMMON4 = new AdditionalActionId(action++, "TEST");

    // Must be LAST
    public static final AdditionalActionId ABON_RECALC_ALL    = new AdditionalActionId(1000000, "Дополнительное действие");
}

// Пример перебора всех значений
/*Iterator it = AdditionalActionId.getIterator();
while (it.hasNext())
{
    action = (AdditionalActionId)it.next();
    print("ID: " + action.toInt() + ", desc: " + action.toString());
}
*/

// Пример обращения к конкретному элементу
/*AdditionalActionId action = AdditionalActionIds.TARIFF_ADD_COMMON3;
print("ID: " + action.toInt() + ", desc: " + action.toString());*/

Иначе имеем проблемы. Не знаю, бага или фича, но факт.

Nov 19, 2010 - 1 minute read - projects programming

Crowns 0.6.0

Выпустил версию 0.6.0 программы Crowns.

Из основных изменений:

  • Добавлена возможность задавать ограничение на отображение деревьев по возрасту (кстати это поле, как я понял, в основном используется не для возраста, а для задания года “рождения” дерева)
  • Исправлен диалог редактирования данных: в нем невозможно было задать возраст дерева

Nov 13, 2010 - 1 minute read - linux programming

MountTray

Написал небольшую программу для подключения, отключения сменных дисков в Linux, использует udisks для монтирования (в планах сделать бакенд и для простых mount/unmount через sudo, класс для использования которого уже написан, для полных минималистов), udev для детекта новых дисков и изъятия существующих (опять таки, готов и бакед для использования inotify, можно будет альтернативно прикрутить его, кстати, а на други unix системах как с inotify?) и в минимальном плане DBus для возможности получать сообщения: а вдруг кто-то там снаружи примонтировал/отмонтировал диск.

Пожелания - мне на мыло

Проект разместил на Gitorius: http://gitorious.org/h4tr3d-utils/pages/MountTray

Новый адрес: https://github.com/h4tr3d/mount-tray, проект не разрабатывается уже несколько лет.

Nov 11, 2010 - 2 minute read - туризм

Планировали одно, а получилось как получилось...

В составе трех человек:

  • Быстрый (Серёга)
  • Медленный (Я)
  • Новичёк (Настя) планировали на длинный ноябрьских праздниках сгонять траверс Фалаза-Пидан, и даже собрались и даже выехали…

…Но сходили, категорически не так как ожидали Но не это главное, а главное осюсения (коих, в отличии от почти диванного похода на Облачную, про который даже написать то нечего в голову не лезет, значительно больше)

В общем, маршрут не прошли, причина проста: “Русские на войне своих не бросают…” А если проще: Настя конкретно натерла (до крови) ноги уже к тому моменту как дошли до ушуисткой базы (да, решили идти по Смольному). Поэтому сделали привал, стали держать военный совет: одну домой её не отправим и одному продолжать маршрут тоже не есть гуд. Поэтому пошли на компромисс: в первый день пробатонились чуть дальше по ручью - разбили лагерь, дышали воздухом, гоняли чаи. На следующий день Я и Серёга сгоняли в радиалку на Фалазу…

Тут тоже весело получилось: планировали топать по тропе (ну вроде как она там есть), хотя я в тех районах уже на Фалазу забирался, но поднимался по осыпям. В общем… девиз радиалки стал: “тропа!? какая тропа! идём по приборам!”

Прибора было три: распечатка карты, компас и “что-то мне подсказывает”. Последним пользовался чаще :laughing:

Тропу даже искать, в общем, не стали: планировалось выйти на седловинку и там подниматься - проверить состояние и что там вообще за условия, но обходя заросли актинидии пополам с малиновым вар… в смысле с элеутерококком, опять выбрались на осыпь, ну уже и решили по ней и забираться. Так и доползли, встречая, изредка, творения рук человеческих в виде туров ( http://picasaweb.google.com/skoulik/yZvOXG#5536649695455909314).

Немного почалившись на вершине, вдыхая воздух, решаем таки на обратном пути свалиться в седловинку и дальше по ручью. Ну что, сказано, сделано - на седловинку попали как снайперы (склон с осыпями и голый лес помогают ориентироваться), про тропы уже не думали (ибо нефиг), проходим немного и начинаем спускаться к ручью… От тут начинается красоты: толстенные поваленные деревья обросшие мхом, валуны в таком же состоянии, актинидия, которая, цепляясь за ноги, как бы намекает на слова из песенки (хоть там и морская тематика): “…оставайся мальчик с нами, будешь нашим королём…”, ну и конечно - элеутерококк :simple_smile: Скоро и сам ручей появился валуны стали более красивыми, а мы, как горные козлы, устроили горно-лесной паркур - скакали по камням (без рюкзаков, чего бы это не поскакать-то)

В лагере нас ждал обедс, покушали, собрались и обратно на электричку. Иголки от элеутерококка доставал до вчерашнего дня

И нужно будет опять выбрать время, маршрут таки сходить.

Малость фото (в художественном оформлении, ну уж какова фантазия художника):

Nov 11, 2010 - 3 minute read - туризм

В гостях у зимы. Или Облачная оправдывает своё название

15-16-17 октября побывали наконец на Облачной. Всеми красотами насладиться не получилось, ибо сопка в точности до каждой буквы оправдала своё название: вершина порадовала плотным туманом-облаком, хотя на высоте 1000 метров было ясно. Пока добирались до кордона Уссури встретили первый снег… За три километра до шлагбаума размытый мост и крайне неудачный объезд остановил наш автобус, но не наши ноги. Из-за этой мелочи, было накручено в каждую сторону лишних 13-14 км (на обратном пути я что-то вообще чуть не помер, но дополз, ибо нефиг).

Итак, наш путь начался с мокрого снега переходящего в дождь: не пожалел, что взял с собой пончо. Добравшись до кордона руководитель всего этого мероприятия (упали на хвоста мегафоновцам, они заказывали автобус, разрешение на въезд на территорию парка “Зов тигра”) быстро уладил дела с егерями и мы двинулись дальше.

Так потихоньку продвигаясь добрались сначала к свороту с дороги к подножию, а пока шли, познакомились с двумя парнишками - аукнулась поездка в Бастак, там с нами была девушка - Полина, это оказались её знакомые (видели меня на фотографиях). Воистину планета квадратная, а туристические тропы - тесные :)

Добравшись до стоянки у, фактически, самого подножия, сделали небольшой привал на чай, сугрев и перекус. Накормили и обогрели отставших от основной группы (мы, в составе: я, Серега, Настя, её матушка, Ярослава, Лиза и Леха, шли отдельной бандой со своими продуктами и целями). Ну а дальше был последний рывок, подъем по старой лесовозной дороге до высоты около 1000 метров, где были около семи часов вечера субботы, разбили лагерь, организовали ужин, а во круг снег лежит: привет Зима!

На стоянке проблемы с водой - или далеко вниз спускаться или далеко вверх до родника подниматься. Ситуацию спас снег и огромное количество сушняка для дров.

Ужин, сон (да, летняя палатка, с поднятыми пологами оказалась уже не совсем для этого времени года).

Утром ранний подъем, завтрак и подъем на легке до вершины.

Подъем до плато занял около часа или полутора… Ещё на подъеме ясная погода сменилась туманом. На плато, если бы не туры, было бы очень сложно ориентироваться. Хотя даже они не помогли в один момент как и не помог GPS, зато спасли сфотографированная карта и компас :)

Ветренно… Холодно… “Горка” не продувается, это спасает, хотя длительные остановки чреваты. Ярослава тем временем начинает подмерзать, отдаю ей свои перчатки, ибо теплее в них. На последнем перешейке отдаю ей возвращающейся первой партии, как оказалось - правильно.

После последнего перешейка, рывок, ещё один… На последнем пожъеме встречаю спускающихся Лизу и Леху, забираю у них термос с остатками чая. Тихо и мирно добираюсь до самой макушки… Уххххху! Ветер такой, что даже мою тушку в 90 кг порывается положить к земле, видимость - метров 20, не больше. В общем - зашибись и отлично :)

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

Спустившись вниз ждал уже чай кофе сиг… ну вы поняли. Правда передохнуть почти не получилось - быстрые сборы и обратный путь до автобуса…

Что-то он дался мне крайне тяжко, были мысли бросить всё и остаться ещё на одну ночевку… Но ничего отделавшись шикарнейшими мозолями, в темноте при свете луны таки добрались до автобуса. Хотя остаться ещё было бы более прикольно :)

Дома были около пяти утра понедельника, уставшие, но довольные.

В общем как-то так. Получилось довольно диванно, впечатлений не так много. По ссылкам ниже немного фото:

Nov 9, 2010 - 1 minute read - linux

bgrep - binary grep

Уже решил было сам писать, но оказалось, что уже сделано: http://debugmo.de/2009/04/bgrep-a-binary-grep/

Выкачиваем с git-hub: git clone https://github.com/tmbinc/bgrep.git

Строим: gcc -o bgrep bgrep.c

Далее копируем bgrep куда нибудь, где она будет доступен при поиске в PATH (у меня это ~/bin) и пользуемся примерно так: find . -name ‘*.bin’ | xargs bgrep FFFE003454

или так: bgrep FFFE file.bin

Вывод на экран: имя файла: смещение

Смещение - относительно начала файла в HEX виде. Далее mcview, biew или любой HEX редактор-просмотровщик в помощь.

Nov 1, 2010 - 3 minute read - programming c++

QFileDialog с Image Preview: метод хакера

В Qt3 была чудная возможность в диалоге открытия интегрировать виджет в котором осуществлять предпросмотра содержимого файла, в частность делать предпросмотр изображений. В Qt4 такую возможность убрали. Да, можно приделать при помощи ItemDelegate кастомное отображение для иконок, и тем самым осуществлять предпросмотр, но не всегда это бывает удобным. Следующий вариант: писать свою реализацию класса для диалога открытия/сохранения. Я же решил попробовать хитрый способ…

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

Итак смыл: наследовать свой класс от QFileDialog, в конструкторе при помощи findChild() по имени искать контейнер в котором располагается виджеты для отображения иконок и быстрых ссылок, приводить его к нужному типу, создавать виджет для предпросмотра, добавлять его в контейнер, при смене файла обновлять содержимое.

Для того, что бы узнать какой контейнер используется (что бы произвести приведение типа) и его имя, нам потребуются исходники Qt, которые скачиваем с сайта Nokia: http://qt.nokia.com, распаковываем, находим файл *qfiledialog.ui// и загружаем его в Qt Designer. После пары минут изучения находим, что контейнер это компонент типа QSplitter и имя у него - splitter. Всё просто и лаконично.

Теперь создаём свой класс, наследуем его от QFileDialog

class MFileDialog : public QFileDialog

В теле класса обязательно ставим Q_OBJECT

в секции private: создаем прототип функции init() - её будем вызвать из конструктора (если вы решитесь объявить их несколько), и указатель на экземпляр класса QLabel - его будем использовать для вывода миниатюры изображения:

private:
    void init();
    QLabel *_preview;

Да, я обычно использую для методов класса одну секцию private/public/etc, а для переменных - другую. Так получается нагляднее.

Далее, сразу вспоминаем, что нам нужно обновлять предпросмотр при смене файла, для этого создадим слот - обработчик сигнала currentChanged(**QString**):

public slots:
    void fileChanged(const QString &file);

Реализации методов init() и fileChanged(**QString** &file) в листинге ниже:

void MFileDialog::init()
{
    _preview = 0;

    // HACK: original file dialog in Qt4 does not have preview functinality
    // Work well on 4.7.1
    QSplitter *splitter = findChild<QSplitter*>("splitter");
    if (splitter == 0)
    {
        return;
    }

    _preview = new QLabel();
    QRect geomerty = _preview->geometry();
    geomerty.setWidth(160); // Настройки геометрии, можно так же сделать что бы рамочка отображалась или что-то вроде 
    _preview->setGeometry(geomerty);

    splitter->addWidget(_preview); // Собсвенно, добавляем наш виджет в контейнер

    // подключаем обработчик сигнала currentChanged
    connect(this, SIGNAL(currentChanged(QString)),
            this, SLOT(fileChanged(QString)));
}

void MFileDialog::fileChanged(const QString &file)
{
    if (_preview == 0)
    {
        return;
    }

    QPixmap pix(file);
    if (pix.isNull())
    {
        _preview->setPixmap(QPixmap());
    }
    else
    {
        // Не зыбываем масштабировать изображение к размеру нашей области просмотра 
        QSize size = _preview->size();
        pix = pix.scaled(size, Qt::KeepAspectRatio);
        _preview->setPixmap(pix);
    }
}

Не забываем в конструкторе вызвать init(), а в деструктор поместить delete _preview. И всё, примерно так это выглядит:

При этом, если даже не будет найдет разделитель с таким именем, диалог не поломается - просто не будет отображаться окошко предпросмотра.

Какие улучшения тут можно сделать? Я думаю как минимум такие:

  1. Установление ограничение на размер файла для которого будет автоматически делаться предпросмотр, ибо загрузка занимает время, а файлы могут быть оооочень большими (у меня, к примеру, есть такие которые не отрываются по причине нехватки памяти).
  2. Вместо QLabel использовать QToolButton и если файл слишком большой отрабатывать нажатие и делать принудительный предпросмотр, либо какие-то другие действия.

PS Кому нужны исходники, пишите на мыло.

Oct 23, 2010 - 2 minute read - programming c++

Qt4 и Custom Types

Известно, что, без преувеличения, основа Qt это moc и их система метатипов. В базе метатипов зарегистрированы все простые C/C++ типы и все сложные Qt типы, часто этого бывает с головой достаточно для написания программ: при использовании QVariant и QSettings или при организации вызовов типа сигнал-слот…

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

qRegisterMetaType<PhotoFormat>("PhotoFormat");

После этого его можно использовать в качестве параметра в механизме сигнал-слот.

Следующим полезным шагом - приведение нашего типа к QVariant, для этого, в дополнение к предыдущему, в заголовочном файла где располагается объявление нашего типа (на самом деле - в любом месте, но так будет логичнее) нужно разместить такую конструкцию:

Q_DECLARE_METATYPE(PhotoFormat);

Всё, после этого можно использовать подобные конструкции:

QVariant var = qVariantFromValue(PhotoFotmat("Name", 23, 22));
PhotoFormat format = qVariantValue<PhotoFormat>(var);

Ну и последнее, сохранение и восстановление нашего класса при помощи QSettings. Для этого нужно, для начала, зарегистрировать потоковые операторы:

qRegisterMetaTypeStreamOperators<PhotoFormat>("PhotoFormat");

Далее, обычно в заголовочном файле, где объявлен класс, объявляются операторы << и >>:

QDataStream &operator<<(QDataStream &out, const PhotoFormat &obj);
QDataStream &operator>>(QDataStream &in, PhotoFormat &obj);

Обращаю внимание - за пределами class {};

А реализация примерно такая:

QDataStream & operator <<(QDataStream &out, const PhotoFormat &obj)
{
    QString name = obj.getFormatName();
    QSizeF  size = obj.getSize();
    qreal   dst1 = obj.getTopPateDistance();
    qreal   dst2 = obj.getNoseChinDistance();

    out << name << size << dst1 << dst2;
    return out;
}

QDataStream & operator >>(QDataStream &in, PhotoFormat &obj)
{
    QString name;
    QSizeF  size;
    qreal   dst1;
    qreal   dst2;

    in >> name >> size >> dst1 >> dst2;

    obj.setFormanName(name);
    obj.setSize(size);
    obj.setTopPateDistance(dst1);
    obj.setNoseChinDistance(dst2);

    return in;
}

По сути, сериализация класса. Более подробная информация - на странице справки по классу QMetaType, и можно глянуть тут: http://www.crossplatform.ru/?q=node/281

Oct 23, 2010 - 2 minute read - programming c++

Qt4 on Mac OS X

Делал тут небольшую работку, писал программку, которая позволяла таскать линии по холсту и формировать по его данным потом xml файл в заданном формате, который потом превращался в уровень для игры. Заказчик просил ObjectivC и целевая платформа Mac OS X, на мой вопрос о критичности ObjectvC ответил - не суть, предложил написать на Qt4, тем самым покроем, одним махом, три платформы: Linux/Windows/Mac, но будет нуанс - я только примерно знаю как собирать под Mac, в общем, подумав - согласились.

Работу, естественно, я делал под Linux, проверил сборку для Windows (опять таки - кросскомпиляция). Показал результаты (сборку под Win32 и скапченый видеоролик работы в Linux), заказчику понравилось, дальше пошла очередь запуска на Mac OS X…

Первым делом был скачан Qt SDK 2010.05 - бронебойно, но зато всё сразу есть. Собрать получилось без проблем (были ворнинги у линковщика на лишние пути, указанные через атрибут -L, которые реально не существуют, но тут камень в огород создателям SDK). А вот дальше встал вопрос - а как запускать приложение на машинах где нет Qt4? Под windows/linux, в случае распространения бинарников, можно просто положить необходимые dll/so рядом и исполняемым файлов, но в Mac OS X они распространяются в виде бандла (директория с определённой структурой и суффиксом .app). Так вот, что бы собрать всё нужные библиотеки в бандл, разработчики Qt сделали утилиту macdeployqt, которую, после сборки, достаточно натравить на полученное приложение так:

macdeployqt helloworld.app

Тут человек с ником Ayoy, тоже озадачивался подобными вопросами, и рассмотрел более подробно. Так же, он столкнулся с тем, что если используются библиотеки/фреймворки которые используют Qt, после обработки macdeployqt они будут продолжать ссылаться на системные, а не те, что в бандле. Что бы это исправить он написал скрипт на перл, которые позволяет это исправлять: http://gist.github.com/109674

Чуть позже поставлю в виртуалку Mac OS X, посмотрю, может на будущее буду сам делать бандлы, а ещё заинтересовала кросс-компиляция для макоси :)