Инструменты пользователя

Инструменты сайта



// Qt5, OpenGL и Ubuntu/Mint

Временами, при сборке ловится такое:

/usr/bin/ld: cannot find -lGL
collect2: error: ld returned 1 exit status

При этом в системе наблюдается такое:

$ locate libGL.so
/usr/lib/i386-linux-gnu/mesa/libGL.so.1
/usr/lib/i386-linux-gnu/mesa/libGL.so.1.2.0
/usr/lib/insync/libGL.so.1
/usr/lib/nvidia-346-updates/libGL.so
/usr/lib/nvidia-346-updates/libGL.so.1
/usr/lib/nvidia-346-updates/libGL.so.346.82
/usr/lib/x86_64-linux-gnu/mesa/libGL.so
/usr/lib/x86_64-linux-gnu/mesa/libGL.so.1
/usr/lib/x86_64-linux-gnu/mesa/libGL.so.1.2.0
/usr/lib/x86_64-linux-gnu/primus/libGL.so.1
/usr/lib32/nvidia-346-updates/libGL.so
/usr/lib32/nvidia-346-updates/libGL.so.1
/usr/lib32/nvidia-346-updates/libGL.so.346.82

Т.е. вроде всё есть, но чего-то не хватает. А не хватает того, что они живут в директориях, которые не видны линковщику. Для рантайма средство есть через aternatives:

x86_64-linux-gnu_gl_conf
x86_64-linux-gnu_egl_conf

Для девелопмента должен стоять пакет libgl1-mesa-dev. После его установки станет так:

$ ls -l /usr/lib/x86_64-linux-gnu/libGL.so
lrwxrwxrwx 1 root root 13 марта 12 08:47 /usr/lib/x86_64-linux-gnu/libGL.so -> mesa/libGL.so

Проблема в том, что пакет стоит, а файла нет… Скорее всего были какие-то нюансы при обновлении или ещё чего. Лечится:

sudo apt-get install --reinstall libgl1-mesa-dev

// Внешний вид шрифтов Qt 5.4.1 в LinuxMint/Cinnamon

В Qt 5.4.1 вернули рендеринг шрифтов в Linux а-ля Qt4. Поставил Qt 5.4.1 (отсюда), собрал Qt Creator из git, запустил, шрифты явно не похожи на то, что используется в системе (Linux Mint 17.x/Cinnamon).

Поиск в инетрнетах привёл на ссылку: https://forum.manjaro.org/index.php?topic=14931.0

Если коротко, то установка переменной окружения:

export XDG_CURRENT_DESKTOP=GNOME

волшебным образом преобразует внешний вид приложения. Будет хорошим тоном прописать это в qtcreator.sh или аналогичный враппер для приложения, а не делать общесистемным параметром.

По умолчанию этот параметр для Cinnamon содержит: X-Cinnamon, что соответствует спекам XDG.

Стоить отметить, что после перезагрузки всё стало на свои места и без этого параметра. Но если проблема будет проглядываться - попробуйте такой вариант.

// Qt4 и стандартные иконки

Бывает необходимость в вашем приложении отобразить ту или иную стандартную системную иконку, типа значка диска CD-ROM. Можно, конечно, создавать свой ресурсный файл и ложить иконки туда, но это будет выглядеть несуразно, особенно при смене различных тем оформления. Задаёмся резонным вопросом: а как бы использовать то, что уже есть в Qt и/или системе?

Вариант 1: стандартные ресурсы Qt4

Про это написано тут: http://www.qtcentre.org/wiki/index.php?title=Embedded_resources

Помимо списка, приведён и код, которым можно получить список самостоятельно. Так же показан пример, как создавать изображение из ресурса:

QPixmap pixmap(":/trolltech/styles/commonstyle/images/up-128.png");
QIcon   icon(":/trolltech/styles/commonstyle/images/up-128.png");

Вариант 2: стандартные иконки при помощи QStyle

Сылки по теме:

Собственно что нам нужно, это метод класса QStyle:

QIcon QStyle::standardIcon ( StandardPixmap standardIcon, const QStyleOption * option = 0, const QWidget * widget = 0 ) const

Иконки опеределяются перечислением StandardPixmap

Вариант 3: использование иконок темы оформления

Тут, скорее всего, всё будет очень сильно зависеть от платформы. Но в общем случае, нам нужен такой вызов:

QIcon icon(QIcon::fromTheme(QString::fromUtf8("icon-name")));

icon-name имя иконки, возможные значения, стандартные, для Linux можно узнать тут: http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html

// Image Ruler 0.98.1

Выпустил версию 0.98.1 программы Image Ruler для измерения на растровых изображениях.

Изменений минимум:

  • Добавлен вывод суммирующей длинны при измерении ломанной линией (столкнулся с расчётом маршрута по карте)
  • В win32 сборку добавлены плагины графических форматов: теперь из коробки открываются jpeg/gif и иже с ними, иначе работал только png.

// 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 этот функционал бесмесленен, поэтому портабельность не сильно пострадает, при использовании директив препроцессора.

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

// 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:

// 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, проект не разрабатывается уже несколько лет.

// 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 Кому нужны исходники, пишите на мыло.

// 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: qthelp://com.trolltech.qt.470/qdoc/qmetatype.html и можно поглядеть тут: http://www.crossplatform.ru/?q=node/281

// 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, посмотрю, может на будущее буду сам делать бандлы, а ещё заинтересовала кросс-компиляция для макоси :)

Да, в Qt Assistant страница посвященная Mac: qthelp://com.trolltech.qt.470/qdoc/deployment-mac.html версию естественно ставим свою.