Последнее обновление:
November 7, 2019

Есть мысль... Жми, напиши!
Что имеем: Постов : 166 Авторов: 1 Категорий: 27

Qt, QQuickPaintedItem отрисовка в отрицательных координатах (paint outside bounds).

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

Проблема возникла с тем, что по умолчанию всё, что имеет отрицательные координаты x и y будет подвергнуто кастрации.
Да же если поставим:

setClip(false);
setFlag(QQuickItem::ItemClipsChildrenToShape, false);

И попробуем вывести круг:

void MyCircle::paint(QPainter * painter)
{
   QPainterPath path;
   path.addEllipse(-200, -200, 400, 400);

   painter->setPen(Qt::NoPen);
   painter->fillPath(path, QBrush(QColor("orange")));
}

Всё, что меньше (0, 0) будет обрезано:

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

Самое простое решение на данный момент — задать QPainter глобальную матрицу трансформации со смещением в противоположную сторону (что бы не высчитывать новые координаты при отрисовке примитивов). А компоненту задать соответственно в координатах X и Y это же значение смещения. Единственный минус — придётся делать обёртку над компонентом.
Вот так примерно:

painter->setWorldTransform(QTransform::fromTranslate(200, 200), true);
setPosition(QPoint(-200, -200));

И в QML соответственно обёртку сделать:

import QtQuick 2.12
import QtQuick.Window 2.12
import MyCircle 1.0

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("QQuickPaintedItem outside bounds")

    Rectangle {
        anchors.fill: parent
        color: "black"
    }

    Item {
        id: myCircleWrapper
        anchors.centerIn: parent
        width: 200
        height: 200

        MyCircle {
            id: myCircle
            width: parent.width
            height: parent.height
            clip: false
        }
    }
}

И результат:

Как сделать более правильно я, к сожалению, после нескольких дней раздумий и ковырянии исходников Qt не придумал…

Views :

4

Статическая сборка Qt 5.12 из исходников под Windows

Потребовалось мне тут, что бы Qt приложение было в одном exe.  Для этого придётся перекомпилировать Qt.

Качаем: Git

Качаем: Perl, версию 32х битную.

Качаем: Python 2 (У меня 2.7).

Качаем: MinGW, y меня 730 (для распаковки нужен 7Zip, запускать «C:\Programm Files (x86)\7-Zip\7zFM.exe»).

Открываем консольку (Win+R, вводим cmd), прописываем необходимые системные переменные (пути замените на свои):

set PATH=C:\MinGW\mingw730_32\bin\;%PATH%
set PATH=C:\Strawberry\perl\bin\;%PATH%

Переходим в папку, в которой будем развлекаться, качаем исходники Qt:

cd C:/QtStatic
git clone https://github.com/qt/qt5
cd qt5
perl init-repository

Конфигурируем:

configure -opensource -confirm-license -platform win32-g++ -static -release -opengl desktop -qt-zlib -qt-libpng -qt-libjpeg -nomake examples
В конфиге не включён SSL, кому нужно:
Качаем и ставим Win32OpenSSLLite
-openssl -I «c:\Qt\3dparty\openssl-1.0.2m\include» -L «c:\Qt\3dparty\openssl-1.0.2m»
Если вдруг нужно переконфигурировать и пересобрать, то нужно сначала очистить результаты предыдущих потуг:

 git submodule foreach --recursive "git clean -dfx" 
 git clean -dfx

Собираем (Часа 4) и ставим:

mingw32-make -j4
mingw32-make install

После установки настраиваем QtCreator для использования новой версии фреймворка:

В верхней менюшке Инструменты -> Параметры -> Сборка и запуск

Компиляторы -> Добавить
MinGW -> C++
Название: Qt 5.12 Static MinGW
Путь: C:\MinGW\mingw730_32\bin\c++.exe

Отладчики -> Добавить
Название: Qt 5.12 Static DBG
Путь: C:\MinGW\mingw730_32\bin\gdb.exe

Профили Qt -> Добавить
Название: Qt 5.12 Static
Путь: C:\QtStatic\qt5\qtbase\bin\qmake.exe

Комплекты -> Добавить
Название: Qt 5.12 Static
Тип устройства: Desktop
Компилятор C++: Qt 5.12 Static MinGW
Отладчик: Qt 5.12 Static DBG
Профиль Qt: Qt 5.12 Static

 Нажимаем «Применить».

Можно попробовать сделать простенький проект. В параметрах сборки и запуска поставьте «Release» т.к. мы только эту версию собрали.

Ну вот, как-то так =)

Views :

391

Qt, вызов функции с ограничением по времени выполнения (QtConcurrent::run timeout).

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

В общем задача: запустить функцию, по возможности дождаться результат, но не дольше N, если не удалось (время вышло), поставить флаг, что не удалось, что бы потом запросить отдельно.

Можно было бы вообще всё использование вынести в отдельный поток, но городить ради одного места неохото, зато родились вот такие извращения:

  1. В Qt есть QtConcurrent, с помощью которого можно запустить функцию в отдельном потоке,
  2. Так как нам желательно сразу получить результат не выходя из функции (в случае ошибки вызываемая функция особо не задерживается), то можно прописать future.result(), но тогда мы будем ждать до победного, а нам нужно ограничить время выполнения, поэтому
  3. Используем QTimer, что бы по истечению заданного времени завершить выполнение, но и тут облом — мы не можем принудительно завершить выполнение «QtConcurrent::run», (т.к. вызываемая функция может не освободить память/не закрыть доступ к файлу да и вообще хз что). Поэтому завершать выполнение не будем, а тупо забьём на него — пусть там себе в параллельном потоке выполняется, а мы тем временем пойдём дальше, но тогда будет необходимо использовать сигналы, что бы дать понять, когда пора выходить из функции по истечению времени, а когда функция вернула результат, поэтому
  4. Используем QFutureWatcher, что бы подписаться на сигнал «finished» если функция выполнилась.
  5. Ну а что бы ждать сигнала от таймера, либо сигнала о завершении вызываемой функции и не выходить до этого из текущей функции используем  QEventLoop.  Как только тот или другой сигнал будет вызван и обработан, то завершаем цикл.

Из преимуществ: выполнение происходит в отдельном потоке, поэтому не затрагиваем основной процесс, в котором происходит вызов функции.

Из недостатков: мы не управляем параллельным процессом в полной мере, т.е. не можем принудительно завершить процесс и он будет болтаться в памяти до исключения, завершения или посинения (если функция «зациклится»).

В общем, вот так:

#include <QtConcurrent/QtConcurrentRun>
#include <QFuture>
#include <QFutureWatcher>
#include <QEventLoop>
#include <QTimer>
#include <QDebug>

....

void AppCore::runAsync()
{
    qDebug()<<"Run async";

    QString r;
    QFuture<QString> f;

    try {
        f = QtConcurrent::run(this, &AppCore::asyncFunction);
    } catch(...) {
        qDebug()<<"ERROR";
    }

    QFutureWatcher<QString> w;
    QEventLoop l;
    QTimer t;

    connect(&w, &QFutureWatcher<QString>::finished, [&l, &r, &w](){
        r = w.result();
        qDebug()<<"From connection 1.";
        l.quit();
    });

    connect(&t, &QTimer::timeout, [&l, &r](){
        r = "TIME OUT!";
        qDebug()<<"From connection 2.";
        l.quit();
    });

    w.setFuture(f);
    t.start(2000);
    l.exec();

    qDebug()<<"RESULT:"<<r;

}

/**
* @brief Функция, которая будет выполнятся в фоновом потоке
* @return
*/
QString AppCore::asyncFunction()
{
    qDebug()<<"In async function.";
    QThread::sleep(5);

    qDebug()<<"ASYNC FUNCTION COMPLETED!";

    return QString("COMPLETED NORMAL!");
}

Вывод:

Run async
In async function.
From connection 2.
RESULT: "TIME OUT!"
ASYNC FUNCTION COMPLETED!

Т.е. вышло время, мы получили результат, что время вышла, но сама вызываемая функция в параллельном потоке всё равно выполнилась до конца.

Ну и упростим это до макроса, что бы иногда использовать:

/**
* Выполняем функцию в параллельном потоке и отдаём результат,
* если не выполнится за время timeOut_ms, то отдаём timeOut_result
* QtConcurrentRunTimeOut(int, myIntRes, this, &MyClass::myFunction, 2000, -100);
* QtConcurrentRunTimeOut(int, myIntRes, this, &MyClass::myFunctionWithParams, 2000, -100,  14, 88);
* qDebug()<<"RESULT:"<<myIntRes;
* Need include
* #include <QtConcurrent/QtConcurrentRun>
* #include <QFuture>
* #include <QFutureWatcher>
* #include <QEventLoop>
* #include <QTimer>
*/
#define QtConcurrentRunTimeOut(resultType, res, object, functionPointer, timeOut_ms, timeOut_result, ...)\
    resultType _r##res;\
    QFuture<resultType> _f##res;\
    _f##res = QtConcurrent::run(object, functionPointer,##__VA_ARGS__);\
    QFutureWatcher<resultType> _w##res;\
    QEventLoop _l##res;\
    QTimer _t##res;\
    connect(&_w##res, &QFutureWatcher<resultType>::finished, [&_l##res, &_r##res, &_w##res](){\
        _r##res = _w##res.result();\
        _l##res.quit();\
    });\
    connect(&_t##res, &QTimer::timeout, [&_l##res, &_r##res](){\
        _r##res = timeOut_result;\
        _l##res.quit();\
    });\
    _w##res.setFuture(_f##res);\
    _t##res.start(timeOut_ms);\
    _l##res.exec();\
    resultType res=_r##res;

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

Вот как-то так =) 

Views :

123

Linux, USB модем ZTE MF667 от Beeline, разблокировка и подключение через wvdial

В общем, потребовалось заставить это чудо выходить в интернет через МТСовскую симку.

Для начала нужно заставить его определяться как модем, а не как cd-rom и отвязать от оператора (убрать NODOWNLOAD.FLG).

sudo apt-get update 
sudo apt-get install minicom

dmesg | grep attached  # Появится 3 устройства, нужное дальше методом перебора

sudo minicom --device=/dev/ttyUSB1  
ATE  # Должно выдать OK
AT+ZCDRUN=E   # Так же должно выдать OK
ctrl+c # Выходим и перевтыкаем модем, ждём секунд 30, пока не загорит зелёный светодиод, сигнализируя, что мы в сети

Возможно, придётся повторить для всех устройств, которые выдал «dmesg | grep attached».

Ставим wvdial и настраиваем:

sudo apt-get wvdial
sudo nano /etc/wvdial.conf

Сам конфиг wvdial для mts у меня такой:

[Dialer Defaults]
Init1 = ATZ
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
Modem Type = Analog Modem
Phone = "*99#"
ISDN = 0
Password = mts
New PPPD = yes
Username = mts
Modem = /dev/ttyUSB1
Baud = 9600
Country = Russia
Init3 = AT^SYSCFG=14,2,3fffffff,0,1  # Приоритет 3G
Init4 = AT +CGDCONT=1,"IP","internet.mts.ru"

Idle Seconds = 0
Stupid Mode = 1
Carrier Check = no
Auto Reconnect = on

Ну а дальше остаётся только подключиться:

sudo wvdial

Что бы потом не париться с sudo можно wvdial добавить в группу.

Надеюсь, всё удалось =)

 

Views :

93

Bareos 18.2, компиляция из исходников

Понадоиблось тут Bareos под ARM платформу скомпилировать, которая стоит на домашнем NAS.

Впринципе, всё просто, за исключением нескольких мелочей.

Для использования bareos-webui необходимо скомпилить вместе с Jansson, а для этого его нужно получить, собрать, установить и показать конфигуратору bareos где он лежит:

sudo apt-get install pkg-config
sudo apt-get install autoconf
sudo apt-get install libtool
sudo apt-get install build-essential
git clone https://github.com/akheron/jansson
cd jansson
autoreconf -i
./configure
make
make install
cd ../

Если используется Debian 8, то поставим нужный cmake и g++ 6:

echo "deb http://ftp.us.debian.org/debian unstable main contrib non-free" >> /etc/apt/sources.list.d/unstable.list
sudo apt-get install g++-6
sudo apt-get install cmake

sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-6 30
sudo update-alternatives --set c++ /usr/bin/g++
update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-6 20
update-alternatives --set cc /usr/bin/gcc-6

c++ --version //-- Вывод должен быть 6.4.0 (не ниже и точно не 7 или 8 - не скомпилится)
cmake --version //-- Вывод должен быть 3.11.2 (не ниже)

Ну а дальше всё просто — качаем исходники Bareos, компилим, собираем всё это в deb пакет, что бы потом не парится с новыми версиями:

git clone https://github.com/bareos/bareos/ -b bareos-18.2
cd bareos/core
dpkg-checkbuilddeps //-- установите все зависимости из вывода

cp -a platforms/packaging/bareos.changes debian/changelog
VERSION=$(sed -n -r 's/#define VERSION "(.*)"/\1/p'  src/include/version.h)
dch -v $VERSION "Switch version number"
fakeroot debian/rules binary

Если будет ошибка «dpkg-shlibdeps: error: no dependency information found for …«,
то в файле «core/debian/rules» ищем строку «dh_shlibdeps» и дополняем опцией «—dpkg-shlibdeps-params=—ignore-missing-info»

Не забудьте установить новый пароль для root в mysql!

Устанавливаем теперь созданные пакеты:

cd ../

//-- Всё, что нужно для директора (bareos-dir):
apt-get install dbconfig-common
dpkg -i bareos-database-common_18.2.3_armel.deb
dpkg -i bareos-database-mysql_18.2.3_armel.deb
dpkg -i bareos-database-tools_18.2.3_armel.deb
dpkg -i bareos-director_18.2.3_armel.deb
dpkg -i bareos-bconsole_18.2.3_armel.deb

//-- Хранилище (bareos-sd):
dpkg -i bareos-storage_18.2.3_armel.deb
//-- Клиент (bareos-fd)
dpkg -i bareos-filedaemon_18.2.3_armel.deb
dpkg -i bareos-client_18.2.3_armel.deb

Запускаем:

sudo service bareos-sd start
sudo service bareos-fd start
sudo service bareos-dir start

Если будет ошибка «/sbin/bareos-dir: symbol lookup error: /sbin/bareos-dir: undefined symbol: _Z16DbSetBackendDirsP5alist»
то тогда нужно чутка изменить ссылки, что бы bareoscats.so.18 указывала на backends/bareoscatsmysql.so  — пять часов на выяснение причин потратил.

Теперь поставим «Bareos WEBUI».
Ставим необходимые для сборки пакеты и аналогично собираем в deb пакет:

wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg
echo "deb https://packages.sury.org/php/ jessie main" > /etc/apt/sources.list.d/php.list
sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates
sudo apt-get install autotools-dev apache2-dev apache2-prefork-dev
sudo apt-get install php7.1-cli php7.1-curl php7.1-fpm php7.1-gd php7.1-intl php7.1-json php7.1-mbstring php7.1-mcrypt php7.1-pdo-mysql php7.1-xml php7.1-zip
sudo apt-get install libapache2-mod-php7
service apache2 restart
cd webui
dpkg-checkbuilddeps
cp -a packaging/obs/bareos-webui.changes debian/changelog
fakeroot debian/rules binary

Устанавливаем созданный пакет:

cd ../
dpkg -i bareos-webui_15.2.1_all.deb

Ну и можно зайти, в браузере набираем:

http://127.0.0.1/bareos-webui

По настройке будет отдельный пост.

Вот и всё =)

Views :

171

Вылеты Q_ASSERT(c->sender == q_ptr);

Появилась достаточно странная бага — вылеты при отладке и чуть реже при релизе с этой ошибкой.

Сколько бы я не пытался выяснить конкретное место ошибки — везде был облом, пока не понял, что недавно одна из библиотек обновилась.

Чтож, виновник теоретический найден, но вот поиски что именно не так доставило несколько геммора. 

В общем причина была в незакрытом  #pragma pack(8), заменим его на #pragma pack(push, 1)  тем самым запомнив предыдущие настройки, а после объявлений структур восстановим настройки #pragma pack(pop) что бы Qt сума не сходил.

 

 

Views :

117

Модульное тестирование (Unit test) в Qt

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

И так, имеем проект с подпроектами. Хотим тесты. 

Следуя мануалу Qt нужно создать новый проект Unit test, в нём класс для теста, с помощью мастера создания Qt сделает базовый скелет.

Но вот тут начинаются проблемы. Следуя задумке Qt:

  1. Один проект — один класс для теста. Мда…
  2. С помощью макроса QTEST_MAIN (или QTEST_APPLESS_MAIN) создаётся функция main.
  3. Поэтому каждый проект соответственно компилируется в бинарник
  4. Соответственно каждый тест запускается отдельно и не зависимо от други

Мне кажется такая организация немного не логичной.  

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

Но так нужно, что бы в QtCreator работала панель «Tests», в которой удобно просматривать результаты тестирования и выборочно запускать сами тесты, вот так это выглядит:

Можно, конечно,  реализовать все классы тестов просто в  отдельной папке проекта (например Tests), убрать все макрос QTEST_MAIN, и запускать все тесты из функции main основного проекта, но тут минус в том, что они будут выполняться при любом запуске проекта, ну и выборочный запуск будет работать через жопу, либо вообще не работать — только все сразу. 

Вот так, например:

void main()
{
    ...
    MyTestClass1 tc1;
    if (QTest::qExec(&tc1, argc, argv)>0) return 42;
    
    MyTestClass2 tc2;
    if (QTest::qExec(&tc2, argc, argv)>0) return 42;
    ...
}
        
        
        

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

Кому достаточно такого поведения, можно дальше не париться =)

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

Решение такое:

Сделаем наш файл проекта (*.pro) подключаемым/шаблонным (*.pri) что бы его можно было инклудить в тестовые проекты,
для этого в *.pro  файле убираем из SOURCES  main.cpp (её мы будем подключать в тестовые проекты отдельно) и всё из *.pro файла переносим в *.pri файл (сначала создав обычный пустой текстовый файл),
потом в *.pri файле в самом верху прописываем:

VPATH += $$PWD #Что бы файлы подключались относительно оригинального расположения файла
INCLUDEPATH += $$PWD

В *.pro файле основного проекта прописываем:

include(common.pri)  #Подключаем основной класс проекта
SOURCES += main.cpp \  #Как обычно добавим в исходники наш основной main

Ну и теперь осталось изменить немного функцию main, что бы когда она была в основном проекте, то запускалась бы как обычно, а когда была в тестовом проекте, запускала бы тест.

#include <QApplication>
#include <QQmlApplicationEngine>
......

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main/main.qml")));
    .....

   //-- Даём возможность инклудить нас в тесты
    #ifdef MY_TEST
        return runTest(argc, argv);
    #else
        return app.exec();
    #endif
}

С помощью проверки, объявлен ли макрос запускаем на исполнение тест, иначе всё как обычно.

Такой финт ушами нужен, потому что QTestLib сканирует классы на наличие  «QTest::qExec(….)» и, если у нас этого не будет в самом классе, то тест не будет доступен в панельке, поэтому объявление функции запуска будет находиться в самом тестовом классе, а запуск этой функции из main.

Так, теперь как использовать:

В основном проекте создаёте проект с поддиректориями, называете, например «Tests», в этом подпроекте с помощью мастера создания создаёте уже проект «Unit test», например «test1».

В *.pro файле (у нас «test1.pro») тестового проекта прописываем:

include(../../MyProject/common.pri)

И в *.cpp тестовом классе подключаем нашу main функцию для запуска основного проекта и теста:

int runTest(int argc, char *argv[]) //-- Нужно, что бы парсер тестов нашёл этот тест, поэтому запускаем мы его из main
{
    MyTestClassName t;
    return QTest::qExec(&t, argc, argv);
}

#define MY_TEST
#include "../MyProject/main.cpp"

Надеюсь, принцип понятен. Только с относительными путями не наебитесь 😀

Вот теперь всё тестится, управляется, и да же желание тесты писать появилось =)

 

 

 

Views :

848

Использование интерфейсов классов в Qt и QML

Привет!

Порою удобнее в QML работать именно с интерфейсом класса, а так же иметь возможность засунуть его в QVariant.  

Разумеется простым способом в «лоб» не получится, т.к. Qt в QML работает с QObject, а мы от него не унаследовались и никакой информации для метасистемы не дали.

Долго я копался в недрах метасистемы Qt, уж собирался делать костыли, но наткнулся на макрос Q_DECLARE_INTERFACE.

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

Пишем:

#ifndef IINTERFACE_H
#define IINTERFACE_H

#include <QObject>


class IInterface {
public:
    virtual QString myProperty() const =0;
    virtual void setMyProperty(QString myProperty) =0;
    virtual void myPropertyChanged(QString myProperty) =0;
};

Q_DECLARE_INTERFACE(IInterface, "pavelk.iinterface")


#endif // IINTERFACE_H

Обратите внимание на строку с «Q_DECLARE_INTERFACE» — тем самым мы даём понять метаобъектной системе Qt, что это интерфейс, что бы он прописал необходимые функции по получению исходного класса.

Ну и сам класс:

#ifndef MYCLASS_H
#define MYCLASS_H

#include <QObject>

#include "iinterface.h"

class MyClass : public QObject, public IInterface
{
    Q_OBJECT
    Q_INTERFACES(IInterface)
    Q_PROPERTY(QString myProperty READ myProperty WRITE setMyProperty NOTIFY myPropertyChanged)
public:
    explicit MyClass(QObject *parent = nullptr);

    QString myProperty() const;

signals:
    void myPropertyChanged(QString myProperty);

public slots:
    void setMyProperty(QString myProperty);

private:
    QString m_myProperty;
};

#endif // MYCLASS_H

Обратите внимание на строку с «Q_INTERFACES(IInterface)» — тем самым мы даём знать Qt как именно преобразовывать этот класс к указанному интерфейсу. Кстати, можно наследоваться сразу от нескольких интерфейсов — просто пропишите через пробел их все.

Ну и теперь самое интересное. Преобразовываем класс к интерфейсу и передаём этот интерфейс в QML

   MyClass * myClass = new MyClass(0);
   IInterface * interface = myClass;
   engine.rootObjects().at(0)->setProperty("myClassInterface", qVariantFromValue( dynamic_cast<QObject*>(interface) ));

Напрямую в QVariant интерфейс засовывать нельзя, т.к. он не знает необходимой информации, поэтому преобразовываем сначала в QObject*, а благодаря тому, что мы прописал Q_DECLARE_INTERFACE метасистема знает как работать с этим интерфейсом.

Полный код примера тут:  https://github.com/Riflio/QMLInterfaces

Вот как-то так =)

Views :

458

NGINX location alias + php + rewrite (try_files)

Понадобилось мне как-то у домена сделать алиас на совсем другую директорию, которая была вне root, помучался немного и вот что получилось:

Прописываем в секции server {}:

location /bareos-webui/ {
    alias /usr/share/bareos-webui/public/;
    autoindex on;
    try_files $uri $uri/ /bareos-webui//bareos-webui//index.php?$query_string;
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+?\.php)(/.*)?$;
        fastcgi_pass unix:/run/php/php7.0-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $request_filename;
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

Вся проблема оказалась в том, что как-то странно работает try_files вместе с alias…, т.е. нужно два раза повторить адрес из location.

Угробил бля 3 часа на эксперименты… Видимо баг самого NGINX.

Views :

519

AVFrame(AVPicture) конвертация в OpenCV::Mat

Понадобилось тут мне сконвертировать  AVFrame в Mat для дальнейших издевательст с помощью OpenCV, загуглил я это дело, и нашёл кучу способов.

Вот один из них:

void AVFrameToMat(const AVFrame * frame, Mat& image)
{
    int width = frame->width;
    int height = frame->height;
    image = Mat(height, width, CV_8UC3);
    int cvLinesizes[1];
    cvLinesizes[0] = image.step1();
    SwsContext* conversion = sws_getContext(width, height, (AVPixelFormat) frame->format, width, height, PIX_FMT_BGR24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
    sws_scale(conversion, frame->data, frame->linesize, 0, height, &image.data, cvLinesizes);
    sws_freeContext(conversion);
}

Мне одному кажется, что столько преобразований излишни?  Ведь OpenCV напрямую позволяет работать со всеми форматами и можно сделать преобразование напрямую! 

В моём случае я работаю с форматом видео YUV420P.

if ( m_picture->format == AV_PIX_FMT_YUV420P ) {
    int numBytes=avpicture_get_size(AV_PIX_FMT_YUV420P, m_picture->width, m_picture->height);        
    uint8_t * buffer = reinterpret_cast<uint8_t *>(av_malloc(numBytes));
    avpicture_layout( ( AVPicture*)m_picture, AV_PIX_FMT_YUV420P, m_picture->width, m_picture->height, buffer, numBytes); //av_image_copy_to_buffer() в новом API


    Mat yuv = Mat(m_picture->height+m_picture->height/2, m_picture->width, CV_8UC1, buffer );
    Mat gray = Mat(m_picture->height, m_picture->width, CV_8UC1, buffer ); //-- Если нужно получить сразу в градациях серого (формат YUV содержит в первом канале как раз в градациях серого кадр)

    Mat rgb = Mat(m_picture->height, m_picture->width, CV_8UC3);
    cvtColor(yuv, rgb, CV_YUV420p2RGB);

    imshow("RGB", rgb);
    imshow("GRAY", gray);
}

ВАЖНО: Не забудьте очистить память из под buffer, так как после удаления Mat она не очищается самостоятельно!

В вашем случае замените форматы на те, с которыми работаете.

Вот как-то так…

 

 

Views :

251