" title="Написать письмо">Написать письмо
Много статей не имеет срока устаревания. Есть смысл смотреть и 2011, и даже 2008 год.
Политика сайта: написать статью, а потом обновлять ее много лет.
Открыта карта ВТБ для материальной поддержки сайта: 4893470220568296.

Рекламодателям! Перестаньте спамить мне на почту с предложениями о размещении рекламы на этом сайте. Я никогда спамером/рекламщиком не был и не буду!
Ваш IP: 54.166.207.223
Вы подарите мне деньги на развитие этого сайта? Номер карты указан зеленым цветом.
 

Статистика

Пользователи : 1
Статьи : 1066
Просмотры материалов : 3669851
 
Бесплатная среда программирования Qt: нюансы (21.08.2017). Печать E-mail
2017 - Август
21.08.2017 20:50
Save & Share
После полугода работы в Qt Creator v.3.5.1 пришло время сделать некоторые выводы. Каждая среда имеет свои тонкости функционирования; и если ранее думал, что самая глючная среда это Borland C++ Builder v.6.0 - то тут Qt переплюнула и его. Если отвлечься и сказать о самой логичной и безглючной среде - то это IBM Lotus Domino Designer, в которую входит язык LotusScript.
В защиту Qt сразу стоит сказать:
- Qt позиционирует себя как бесплатная среда разработки даже для организаций (GPL, LGPL - оплачивается только лицензия техподдержки, если есть желание). В связи с этим приобретает все большую популярность. Ежики плакали, но продолжали жрать кактус: потому что он бесплатный;
- заявлена кроссплатформенность: полная совместимость исходного кода в разных средах Windows/Linux/Unix.

Переход на Qt порождает ломку при миграции: из Linux перенята парадигма "сигналы-слоты", на понимание которой уйдет достаточно много времени. Образно, сигнал - вызываемый метод объекта или функция, слот - метод объекта или функция, которая сработает при вызывании сигнала. Соединяются сигналы и столы функцией connect, для которой тоже не все очевидно. Чтобы только ее было видно в других файлах, нужно постараться. Потом начинаются проблемы с видимостью сигналов и слотов. Потом выясняется, что сигнал и слот должны иметь одинаковое количество и тип входных параметров (согласно справке, нелогичные условия). Любая ошибка приводит к неработоспособности связки "сигнал-слот", компилятор на эту тему ничего не сообщает. Пример корректного выполнения:
connect(ui->action_PrintScreen, SIGNAL(triggered(bool)), this, SLOT(vScreenShot()));
При нажатии на графическом интерфейсе кнопки "Скриншот" сработает метод triggered, который повлечет за собой выполнение функции vScreenShot. Сразу смущает, что triggered имеет входной параметр bool, а функция не имеет - и при этом все работает. Вот и разногласие со справкой, а в справке попадаются нерабочие примеры примерно каждый сотый. А вот если функции vScreenShot сообщить входной параметр, отличный от bool - функция уже не отработает (не вызовется). В общем, только набиванием шишек эта концепция в голове откладывается правильным образом.

Даже если ломка пройдена - впереди ждут много глюков, и именно им посвящена лавина трехэтажного мата (выдержка из готовой программы по работе с платами ввода/вывода):
- проект не работает при нахождении в русскоязычной папке;
- галка "теневая сборка" при переносе проекта с ПК на ПК включается сама - должна быть выключена вручную после переноса, иначе будут проблемы с QFile;
- функции-заплатки для Qt представлены в файле qt.cpp, в т.ч. iMessageBox и dRound - т.к. стандартные работают коряво;
- иконка проекта выставляется в файле проекта, в DISTFILES +=: RC_ICONS += Icon.ico;
- в том же разделе прописывается версия проекта: VERSION=1.0.0.0 #Мажор, минор, релиз, билд;
- проверка на численность: QString::toFloat(&bCorrect);
- скрытие кнопки Maximize формы невозможно - решается фиксированными minimumSize, maximumSize;
- переименование любых файлов проекта производить только в среде программирования;
- жуткие проблемы с ui: чтобы он был виден в сторонних файлах - обязательно функция помещается в класс Form_Main,
а "Form_Main::" приписывается как префикс к функции в стороннем файле;
- при использовании connect число и тип параметров SIGNAL должно быть равно количеству и типу параметров SLOT. Указываются только типы входных параметров. Из-за этого приходится передавать параметры в виде глобальных переменных или создавать свой класс QConnect с нужными свойствами;
- при миграции проекта с Windows на Linux требуется соблюдение условий:
    - опция "Current Directory" в Projects->Manage Kits->Build & Run->General->Projets Directory должна быть изменена;
    - имя проекта не должно содержать скобок и должно заключаться в двойные кавычки;
    - добавление постфикса "-32" в строку Projects->Build Settings->Build Steps->Effective qmake call. Пример: qmake /home/Samokontrol_Linux/Samokontrol_Linux.pro -r -spec linux-g++-32 CONFIG+=debug.
- при миграции проекта с Linux на Windows требуется соблюдение условий:
    - имя проекта не должно содержать точек и должно заключаться в двойные кавычки;
    - часть библиотек может не скомпилироваться нормально, что ставит под угрозу саму задачу миграции;
    - Qt - НЕ КРОССПЛАТФОРМЕННАЯ СРЕДА! При миграции с Windows на Linux даже цвета у кнопок и радиобаттонов исчезли!
        Исправляется записью в main.cpp "qAppl.setStyle("windows");";
    - так и не удалось заставить корректно работать валидаторы (проверка диапазона).
        Частичную работу обеспечивает смена локали: "QLocale::setDefault(QLocale(QLocale::English, QLocale::CyrillicScript, QLocale::UnitedStates));";
        Т.к. InputMask некорректна в использовании при присвоении данных - поля остаются без валидации;
- QTimer способен срабатывать раньше своего периода! В теле функции таймера приходится его притормаживать;
- QTextBrowser не дает возможности корректно проверять количество строк в себе, когда их кол-во 0 или 1;
- реализация полос прокрутки нелогична и сложна - вместо нее используется изменение положения формы при евенте скролла.
    При этом скролл работает с паразитными смещениями - они исправляются в showEvent. В showEvent нельзя закрывать форму.
- QTimer срабатывает при вызванном ->start только тогда, когда код вызывающей функции полностью отработал - глюк.

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

Пока данная пустышка не выкладывается, т.к. для очистка существующего проекта потребуется время.

Итог: если переломаться и изучить эту среду - возможно, переход на нее даст плоды в будущем за счет той же бесплатности и допиленной кроссплатформенности. Однако когда таймер начал вызываться раньше своего периода - чаша терпения почти переполнилась.

(добавлено 23.08.2017) Забыл еще прикольные глюки этой среды, сегодня один такой долбанул:
- можно спокойно выйти за пределы статичного массива, не получив никаких сообщений типа Overflow - и в результате перестанет работать какой-нибудь QStringList, не имеющий к массиву никакого отношения;
- дебаггер отображает неверную информацию по unsigned long int, когда якобы 2 числа разные - а на деле они одинаковые. В итоге пришлось эти переменные записывать в файл - и только там они отобразились корректно.

(добавлено 25.09.2017) Что создание второй формы в проекте, что обмен данными между формами - нетривиален, неочевиден, непонятен. Нигде еще такого гемора не видел.

(добавлено 01.11.2017) Сделать программу, написанную в Qt, независимой от самой Qt - невозможно. Перенос библиотек .so в папку /usr/lib приводит лишь к тому, что ПО не запускается из-за отсутствия плагина XCB, входящего в состав Qt и недоступного для отдельного скачивания. Статическая линковка библиотек Qt (некоторые умельцы смогли-таки это сделать), со слов других программистов, является незаконной (неприменима к лицензиям GPL и LGPL). Поэтому на все ПК, использующие ПО на Qt, обязаны иметь установленную Qt.

(добавлено 09.02.2018) Обновил список вопиющих глюков. К этому добавляется то, что если connect отработал некорректно, не подключился, или слот неправильно расположен в h-файле - не будет вообще никаких уведомлений. Я даже не буду добавлять в архив полезных исходников написанные за год функции: к черту эту среду с ее псевдокроссплатформенностью.

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

#include "qmessagebox.h" //Для работы с MessageBox.
#include "qtimer.h" //Для создания динамических таймеров.
#include "math.h" //Для работы функций fmod и pow.
#include "qfile.h" //Работа с файлами.
#include "form_main.h" //Для connect.
#include "qapplication.h" //Для пути проекта.
#include "time.h" //Для vDelay.

//Переменные для кнопок iMessageBox.
extern const int iMsgBox_OK_Button = pow(2,0);
extern const int iMsgBox_Save_Button = pow(2,1);
extern const int iMsgBox_SaveAll_Button = pow(2,2);
extern const int iMsgBox_Open_Button = pow(2,3);
extern const int iMsgBox_Yes_Button = pow(2,4);
extern const int iMsgBox_YesToAll_Button = pow(2,5);
extern const int iMsgBox_No_Button = pow(2,6);
extern const int iMsgBox_NoToAll_Button = pow(2,7);
extern const int iMsgBox_Abort_Button = pow(2,8);
extern const int iMsgBox_Retry_Button = pow(2,9);
extern const int iMsgBox_Ignore_Button = pow(2,10);
extern const int iMsgBox_Close_Button = pow(2,11);
extern const int iMsgBox_Cancel_Button = pow(2,12);
extern const int iMsgBox_Discard_Button = pow(2,13);
extern const int iMsgBox_Help_Button = pow(2,14);
extern const int iMsgBox_Apply_Button = pow(2,15);
extern const int iMsgBox_Reset_Button = pow(2,16);
extern const int iMsgBox_RestoreDefaults_Button = pow(2,17);

/*Функция MessageBox глючит в Qt - пришлось писать свою функцию.

    Обязательные параметры:
    - qsTitle_Message: заголовок сообщения;
    - qsMain_Message: основной текст сообщения.

    Необязательные параметры:
    - iIcon: иконка сообщения (см. условия);
    - iMode: суммарный код кнопок для пользователя. Для удобства можно складывать переменные кнопок;
    - iTime: время в секундах, когда автоматически закрыть сообщение с возвращением кода кнопки по умолчанию;
    - qsUnique_Button_Text: название для уникальной кнопки. При нажатии возвращает 0.

    Примеры использования:
    - iResult = iMessageBox("Внимание!", "Ошибка случилась.", 3, iMsgBox_OK_Button + iMsgBox_Help_Button, 30);
    - iResult = iMessageBox("Сообщение", "Уведомительный текст.", 1, 0, 0, "Моя кнопка", 1024).
*/
int iMessageBox(QString qsTitle_Message, QString qsMain_Message, int iIcon = 0, int iMode = 1, int iDefault_Button = 0, int iTime = 0, QString qsUnique_Button_Text = "")
{
    QMessageBox qMsgBox;
    QTimer qTimer_Msgbox_Down; //Таймер закрытия окна автоматически с отработкой кнопки по умолчанию.

    qMsgBox.setText(qsTitle_Message);
    qMsgBox.setInformativeText(qsMain_Message);

    switch (iIcon)
    {
        case 0: qMsgBox.setIcon(QMessageBox::NoIcon); break;
        case 1: qMsgBox.setIcon(QMessageBox::Information); break;
        case 2: qMsgBox.setIcon(QMessageBox::Warning); break;
        case 3: qMsgBox.setIcon(QMessageBox::Critical); break;
        case 4: qMsgBox.setIcon(QMessageBox::Question); break;
    }

    QString qsMode = QString::number(iMode, 2); //Можно и через QBitArray.

    for (int i=qsMode.length()-1; i>=0; i--)
    {
        if (qsMode[i]=='1')
        {
            switch (qsMode.length()-1-i)
            {
                case 0: qMsgBox.addButton(QMessageBox::Ok); break;
                case 1: qMsgBox.addButton(QMessageBox::Save); break;
                case 2: qMsgBox.addButton(QMessageBox::SaveAll); break;
                case 3: qMsgBox.addButton(QMessageBox::Open); break;
                case 4: qMsgBox.addButton(QMessageBox::Yes); break;
                case 5: qMsgBox.addButton(QMessageBox::YesToAll); break;
                case 6: qMsgBox.addButton(QMessageBox::No); break;
                case 7: qMsgBox.addButton(QMessageBox::NoToAll); break;
                case 8: qMsgBox.addButton(QMessageBox::Abort); break;
                case 9: qMsgBox.addButton(QMessageBox::Retry); break;
                case 10: qMsgBox.addButton(QMessageBox::Ignore); break;
                case 11: qMsgBox.addButton(QMessageBox::Close); break;
                case 12: qMsgBox.addButton(QMessageBox::Cancel); break;
                case 13: qMsgBox.addButton(QMessageBox::Discard); break;
                case 14: qMsgBox.addButton(QMessageBox::Help); break;
                case 15: qMsgBox.addButton(QMessageBox::Apply); break;
                case 16: qMsgBox.addButton(QMessageBox::Reset); break;
                case 17: qMsgBox.addButton(QMessageBox::RestoreDefaults); break;
            }          
        }
    }

    //В Linux QT не может использовать в case switch переменные.
    if (iDefault_Button==iMsgBox_OK_Button) qMsgBox.setDefaultButton(QMessageBox::Ok);
    else if (iDefault_Button==iMsgBox_Save_Button) qMsgBox.setDefaultButton(QMessageBox::Save);
    else if (iDefault_Button==iMsgBox_SaveAll_Button) qMsgBox.setDefaultButton(QMessageBox::SaveAll);
    else if (iDefault_Button==iMsgBox_Open_Button) qMsgBox.setDefaultButton(QMessageBox::Open);
    else if (iDefault_Button==iMsgBox_Yes_Button) qMsgBox.setDefaultButton(QMessageBox::Yes);
    else if (iDefault_Button==iMsgBox_YesToAll_Button) qMsgBox.setDefaultButton(QMessageBox::YesToAll);
    else if (iDefault_Button==iMsgBox_No_Button) qMsgBox.setDefaultButton(QMessageBox::No);
    else if (iDefault_Button==iMsgBox_NoToAll_Button) qMsgBox.setDefaultButton(QMessageBox::NoToAll);
    else if (iDefault_Button==iMsgBox_Abort_Button) qMsgBox.setDefaultButton(QMessageBox::Abort);
    else if (iDefault_Button==iMsgBox_Retry_Button) qMsgBox.setDefaultButton(QMessageBox::Retry);
    else if (iDefault_Button==iMsgBox_Ignore_Button) qMsgBox.setDefaultButton(QMessageBox::Ignore);
    else if (iDefault_Button==iMsgBox_Close_Button) qMsgBox.setDefaultButton(QMessageBox::Close);
    else if (iDefault_Button==iMsgBox_Cancel_Button) qMsgBox.setDefaultButton(QMessageBox::Cancel);
    else if (iDefault_Button==iMsgBox_Discard_Button) qMsgBox.setDefaultButton(QMessageBox::Discard);
    else if (iDefault_Button==iMsgBox_Help_Button) qMsgBox.setDefaultButton(QMessageBox::Help);
    else if (iDefault_Button==iMsgBox_Apply_Button) qMsgBox.setDefaultButton(QMessageBox::Apply);
    else if (iDefault_Button==iMsgBox_Reset_Button) qMsgBox.setDefaultButton(QMessageBox::Reset);
    else if (iDefault_Button==iMsgBox_RestoreDefaults_Button) qMsgBox.setDefaultButton(QMessageBox::RestoreDefaults);

    if (iTime!=0)
    {
        Form_Main::connect(&qTimer_Msgbox_Down, &QTimer::timeout, &qMsgBox, &QMessageBox::close);
        qTimer_Msgbox_Down.start(iTime*1000);
    }

    if (qsUnique_Button_Text!="") qMsgBox.addButton(qsUnique_Button_Text,QMessageBox::AcceptRole);

    return qMsgBox.exec()/0x400;
}

//Сверхточное сравнение для float и double с избавлением от паразитной погрешности.
bool bIs_Equal(double dValue1, double dValue2, bool bDouble)
{
    if (bDouble && fabs(dValue1-dValue2)<10e-8f) return true; else return false;
    if (!bDouble && fabs(dValue1-dValue2)<10e-4f) return true; else return false;
}

//Округление с погрешностью ~10^-9.
double dRound(double dValue, int iSigns, bool bMode = true)
{
    double dTrue_Round = round(dValue * pow(10,iSigns)) / (pow(10,iSigns));

    if (bMode) return dTrue_Round; //Обычное округление по правилам алгебры.
    else if (dValue-dTrue_Round<0) return dTrue_Round - pow(10,-iSigns);
    else if (dValue-dTrue_Round>0)return dTrue_Round + pow(10,-iSigns); //Обратная логика, для намеренного создания ошибки округления.
    else return dValue;
}

void vDelay(unsigned long iMICROSecDelay)
{
    unsigned long int SEC2NANO=1000000000;
    double diff=0;
    struct timespec ts1, ts2;
    clock_gettime(CLOCK_MONOTONIC, &ts1);

    while(true)
    {
        clock_gettime(CLOCK_MONOTONIC, &ts2);
        diff=(double)(ts2.tv_sec-ts1.tv_sec)*(double)SEC2NANO + ((double)ts2.tv_nsec - (double)ts1.tv_nsec);

        if (diff/1000>=iMICROSecDelay) return;
    }
}

void vScreenShot(QWidget *qWidget, QString qsObject)
{
    QImage qImage(qWidget->size(), QImage::Format_ARGB32_Premultiplied);
    qWidget->render(&qImage);

    int iFile_Number = 1;
    while (QFile::exists("Скриншот " + qsObject + " №" + QString::number(iFile_Number) + ".png")) iFile_Number++;

    qImage.save("Скриншот " + qsObject + " №" + QString::number(iFile_Number) + ".png");

    if (QFile::exists("Скриншот " + qsObject + " №" + QString::number(iFile_Number) + ".png")) iMessageBox("Сохранен файл \"Скриншот " + qsObject + " №" + QString::number(iFile_Number) + ".png\"" + ".", "Скриншот " + qsObject + " сохранен в " + QApplication::applicationDirPath() + "/.",1); else iMessageBox("Ошибка сохранения!", "Скриншот " + qsObject + " не сохранился на диск.",2);
}
Обновлено ( 09.02.2018 20:53 )
 
 

Последние новости

©2008-2018. All Rights Reserved. Разработчик - " title="Сергей Белов">Сергей Белов. Материалы сайта предоставляются по принципу "как есть". Автор не несет никакой ответственности и не гарантирует отсутствие неправильных сведений и ошибок. Вся ответственность за использование материалов лежит полностью на читателях. Размещение материалов данного сайта на иных сайтах запрещено без указания активной ссылки на данный сайт-первоисточник (ГК РФ: ст.1259 п.1 + ст.1274 п.1-3).