вторник, 24 июля 2012 г.

Компиляция проектов Qt версии ниже 4.8.1 с использованием MinGW GCC 4.7.0

При компиляции проектов Qt версии ниже 4.8.1 компилятором MinGW GCC 4.7.0 с использованием флага -std=c++11 некоторые макросы распознаются как пользовательские литералы, что приводит к ошибкам компиляции. Для версии Qt 4.8.1 выпущен патч, а для предыдущих версий предлагается следующий заголовочный файл, корректно (с точки зрения C++11) определяющий нужные макросы:
#ifndef CXX0XCOMPATIBILITY_HPP__20120723__1427
#define CXX0XCOMPATIBILITY_HPP__20120723__1427

/*
 * For compatibility with C++-0x mode in GCC 4.7.x
 */
//-------------------------------------------------------------------------
# ifndef QT_NO_DEBUG
#  ifdef QLOCATION
#  undef QLOCATION
#  define QLOCATION "\0" __FILE__ ":" QTOSTRING(__LINE__)
#   ifndef QT_NO_KEYWORDS
#    ifdef METHOD
#    undef METHOD
#    define METHOD(a)   qFlagLocation("0" #a QLOCATION)
#    endif
#   endif
#  endif
#  ifdef SLOT
#  undef SLOT
#  define SLOT(a)     qFlagLocation("1" #a QLOCATION)
#  endif
#  ifdef SIGNAL
#  undef SIGNAL
#  define SIGNAL(a)   qFlagLocation("2" #a QLOCATION)
#  endif
# else
#  ifndef QT_NO_KEYWORDS
#   ifdef METHOD
#   undef METHOD
#   define METHOD(a)   "0" #a
#   endif
#  endif
#  ifdef SLOT
#  undef SLOT
#  define SLOT(a)     "1" #a
#  endif
#  ifdef SIGNAL
#  undef SIGNAL
#  define SIGNAL(a)   "2" #a
#  endif
# endif

#endif//

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

воскресенье, 26 февраля 2012 г.

Прочистка форсунок стеклоомывателя

При езде в сырую погоду часто случается досадная неприятность: грязью из-под колёс забиваются форсунки стеклоомывателя. После чего на очистку лобового (а у кого и заднего) стекла приходится тратить гораздо больше времени. Разумеется, это очень снижает безопасность вождения. При попытке прочистить каналы форсунок простой проволокой, зачастую, грязь только проталкивается дальше, к трубке подачи жидкости. В худшем случае проволока может обломиться внутри форсунки, и тогда её остаётся только заменить на новую. А до автомагазина ещё надо доехать... Решение по прочистке было найдено неожиданно, и оказалось очень эффективным. Прочищать надо такой специальной стоматологической штуковиной, под названием эндодонтический файл. Можно выпросить его (с подходящим диаметром) в стоматологическом кабинете, а можно купить в магазине, торгующем медтехникой. Цена их крайне мала -- от 10 рублей за штуку, делаются они из прочной, гибкой стали, так что сломать его внутри форсунки почти невозможно. Так же он имеет конусообразную форму, и резьбу, что позволяет вкрутить его в скопление грязи/ржавчины/чем-там-ещё-может-забиться-форсунка, и вытащить всё это богатство наружу. В общем -- рекомендую. Очень полезная штука.

четверг, 29 декабря 2011 г.

Promote widget в Qt Creator: что это такое, и как его использовать

При создании формы графического интерфейса в Qt Creator-е зачастую возникает необходимость изменить поведение размещённых на форме визуальных компонентов. Для этого в нём (Creator-е) существует механизм promote-инга (корректный перевод не знаю). В качестве примера рассмотрим добавление объекту QLabel возможности реагирования на отпускание на нём левой кнопки мыши (ЛКМ).
Выделив необходимый компонент на форме, в меню ПКМ выбираем пункт Promote to.... Откроется меню задания имени класса-обёртки, и его заголовочного файла. Заголовочный файл может находиться и не в рабочем каталоге с файлом формы (*.ui), можно задать ему либо абсолютный путь (возможны проблемы при переносе проекта между платформами), либо относительный, от рабочего каталога. Дадим классу имя TestPromoter (Promoted class name:), его заголовочный файл -- ./TestPromoter.hpp (Header file:). Нажимаем кнопку Add, и QLabel оказывается "обёрнут" в созданный класс promoter-а.
Теперь нужно вручную создать заголовочный файл, и файл реализации нового класса:
#include <QLabel>
#include <QMouseEvent>
//-------------------------------------------------------------------------
/*
 TestPromoter.hpp
*/
//-------------------------------------------------------------------------
class TestPromoter : public QLabel
{
 Q_OBJECT:
protected:
 virtual void mouseReleaseEvent(QMouseEvent* ev);
public:
 TestPromoter(QWidget* parent = 0,Qt::WindowFlags f = 0);
 TestPromoter(const QString& text,QWidget* parent = 0,Qt::WindowFlags f = 0);
};

#include "./TestPromoter.hpp"
//-------------------------------------------------------------------------
/*
 TestPromoter.cpp
*/
//-------------------------------------------------------------------------
TestPromoter::TestPromoter(QWidget* parent,Qt::WindowFlags f) : \
  QLabel(parent,f) \
{
 return;
}
TestPromoter::TestPromoter(const QString& text,QWidget* parent,Qt::WindowFlags f) : \
  QLabel(text,parent,f) \
{
 return;
}
void TestPromoter::mouseReleaseEvent(QMouseEvent* ev)
{
 if(ev->button() == Qt::LeftButton)
 {
  //Код, который нужно выполнить по указанному событию
 }
 QLabel::mouseReleaseEvent(ev);
 return;
}

Таким образом можно реализовать расширение функциональности визуальных объектов Qt Creator-а.

вторник, 8 ноября 2011 г.

Сборка и установка визуальных плагинов Qt под Windows

В процессе работы с библиотекой Qt возникла необходимость создания своих визуальных компонент-плагинов для Qt Creator-а. Эта задача разделяется на следующие: создание класса визуального компонента (как правило, наследника QWidget); создание класса регистрации плагина в Qt Creator-е; обеспечение возможности сборки как release-, так и debug-версии программы, в которой используется данный плагин. Рассмотрим решение каждой из этих задач для Qt 2010.05 for Windows.
Создание класса визуального компонента происходит обычно, за исключением необходимости добавления одного макроса. Выглядит это приблизительно так:
#include <QWidget>
#include <QtDesigner/QDesignerExportWidget>
//-----------------------------------------------
class QDESIGNER_WIDGET_EXPORT NewClass : public QWidget
{
private:
public:
 explicit NewClass(QWidget* parent = 0);
 ~NewClass(void);
};

Саму реализацию, для простоты, не привожу, можно отнаследоваться от QPushButton, чтобы результат был более нагляден.
Класс QDesignerExportWidget через этот макрос обеспечивает доступ Qt Designer-а к создаваемому классу плагина, и позволяет создавать его экземпляры "на лету", в процессе "таскания мышкой компонентов".
Для регистрации плагина создаётся отдельный класс. Вот как может выглядеть его заголовочный файл:
#include <QtPlugin>
#include <QDesignerCustomWidgetInterface>
//-----------------------------------------------
#include "./NewClass.hpp" //Оба класса находятся в одном каталоге
//-----------------------------------------------
class NewClassPlugin : public QObject,public QDesignerCustomWidgetInterface
{
  Q_OBJECT;
  Q_INTERFACES(QDesignerCustomWidgetInterface);
private:
  bool initialized;
public:
  NewClassPlugin(QObject* parent = 0);
  bool isContainer(void) const;
  bool isInitialized(void) const;
  QIcon icon(void) const;
  QString domXml(void) const;
  QString group(void) const;
  QString includeFile(void) const;
  QString name(void) const;
  QString toolTip(void) const;
  QString whatsThis(void) const;
  QWidget* createWidget(QWidget* parent);
  void initialize(QDesignerFormEditorInterface* core);
};
//-----------------------------------------------

О "наполнении" этих функций можно посмотреть официальную документацию, никаких расхождений с ней не наблюдалось.
Теперь приступаем к сборке и установке плагина. Для ясности дальнейших действий немного о структуре разрабатываемого мной приложения, в котором будет использоваться плагин. Приложение состоит из загрузчика, и динамических библиотек-модулей, которые он загружает, предоставляя некое внутреннее API для функционирования. Как загрузчик, так и модули расположены в индивидуальных поддиректориях общей директории проекта. В директории плагина (пусть её название -- newPlugin) создаём файл проекта newPlugin.pro со следующим содержимым:
TEMPLATE = lib
TARGET = $$qtLibraryTarget($$TARGET)
CONFIG += designer plugin
QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/designer

DLL = $$TARGET".dll"

CONFIG(release, debug | release): QMAKE_POST_LINK = xcopy \
.\\release\\$$DLL ..\\loader\\release /Y

CONFIG(debug, debug | release): QMAKE_POST_LINK = xcopy \
.\\debug\\$$DLL ..\\loader\\debug /Y

# Input
HEADERS += ./newClass.hpp \
           ./newClassPlugin.hpp

SOURCES += ./newClass.cpp \
           ./newClassPlugin.cpp

target.path = $$[QT_INSTALL_PLUGINS]/designer
sources.files = $$HEADERS $$SOURCES *.pro
sources.path = $$[QT_INSTALL_EXAMPLES]/designer/newClass
INSTALLS += target sources

В строке 6 определяется переменная с именем создаваемой DLL, которое будет различным для release- и debug-версий. Строки 8-9 и 11-12 производят копирование средствами Windows собранных DLL плагина в соответствующие каталоги загрузчика. После выполнения команд qmake и mingw32-make all нужно произвести установку собранного плагина в соответствующие каталоги Qt Creator-а. Поскольку он собран, как release-версия, то установка производится командой mingw32-make release-install. Удаляется плагин, соответственно, командой mingw32-make release-uninstall.
И заключительный этап -- сборка модуля, использующего плагин. Поскольку в модуле будет использоваться форма, созданная с помощью Qt Creator-а, то явное подключение заголовочного файла плагина не потребуется. Для модуля создаём файл проекта moduleName.pro:
TEMPLATE = lib
TARGET = 
DEPENDPATH += .
INCLUDEPATH += . $$[QT_INSTALL_EXAMPLES]/designer/newPlugin
CONFIG += thread dll rtti stl exceptions

CONFIG(release, debug | release): LIBS += \
-L"../newPlugin/release" -lnewPlugin

CONFIG(debug, debug | release): LIBS += \
-L"../newPlugin/debug" -lnewPlugind

# Input
FORMS += ./formWithPlugin.ui

HEADERS += ./module.hpp

SOURCES += ./module.cpp

Здесь в строке 4 происходит задание директории, в которой находятся заголовочные файлы плагина. Строки 7-8 и 10-11 задают линковку для отладочной (-lnewPlugind) и релизной (-lnewPlugin) версий. В строке 14 подключается файл описания формы, созданной Qt Creator-ом. Выполнив команды qmake и mingw32-make all, получаем работающий и в release-, и в debug-версиях плагин.

Upd. По неясной пока причине статические библиотеки плагина начали собираться с постфиксом 4, т.е. вместо -lnewPlugin создаётся -lnewPlugin4. В файл проекта, использующего плагин, следует внести соответствующие изменения.

пятница, 28 октября 2011 г.

Количество page faults-ов, как индикатор проблем в работе приложения

В разрабатываемой модульной системе возникли проблемы: после нескольких часов непрерывной работы программа падала по исключению std::bad_alloc. А работать она должна в режиме 24/7 в течении нескольких недель. Первое, что я заподозрил, было утечка памяти, либо дескрипторов. Но проверка Process Explorer-ом ни того, ни другого не выявила. И тут я случайно увидел, что счётчик Page Faults принимает какие-то уж совсем запредельные значения, и непрерывно их увеличивает. В конце концов оказалось, что один из старых модулей при работе с потоками вместо join-а выполнял просто detach, что явно не проявлялось до момента падения программы. Ошибка была исправлена, счётчик Page Faults-ов перестал показывать внешний госдолг США, программа перестала падать на ровном месте.

четверг, 13 октября 2011 г.

Установка VirtualBox на Windows7 в случае вынесенного в сеть домашнего каталога пользователя

Решили наши администраторы вынести каталоги пользователей на отдельную машину в сети. Решили -- и вынесли. Всё было бы хорошо, да только некоторые программы отказывались устанавливаться. Одной из них и была VirtualBox. В процессе установки он говорил, что %UserName% не является корректным именем файла, и аварийно завершался. Решение оказалось простейшим: до установки отключить сетевой адаптер. Так я обновил VirtualBox.

понедельник, 26 сентября 2011 г.

Контролируемый выход из цикла в потоке

В своей деятельности я часто использую потоки. Как правило, из библиотеки boost::thread. На пару с boost::bind возможности получаются впечатляющие. Если поток однократно выполняет какое-либо действие, и сам завершается -- всё хорошо. Но зачастую требуется, чтобы поток выполнял это действие в цикле, через заданный временной интервал, и был способен получать уведомления о завершении своей работы от основного потока приложения. Для подобных целей я использую следующий механизм, основанный на совместном использовании boost::shared_mutex и boost::shared_lock:
#define BOOST_THREAD_USE_LIB
//-------------------------------------------------------------------------
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
//-------------------------------------------------------------------------
#include <string>
#include <iostream>
//-------------------------------------------------------------------------
/*
 g++ -Wall -o ./main ./main.cpp -lboost_thread
*/
//-------------------------------------------------------------------------
class Example
{
private:
 typedef boost::shared_mutex WaiterSharedMutex;
 typedef boost::shared_lock<WaiterSharedMutex> WaiterSharedLock;
 typedef boost::defer_lock_t DeferredLock;
 boost::thread workThread;
 WaiterSharedMutex waiterMutex;
 boost::posix_time::time_duration sleepTime;
 void WorkThread(void)
 {
  while(true)
  {
   WaiterSharedLock wsl(waiterMutex,DeferredLock());
   if(wsl.timed_lock(boost::get_system_time() + sleepTime))
   {
    std::cerr << "Forced exit from thread" << std::endl;
    return;
   }
   std::cerr << "Work of thread" << std::endl;
  }
  return;
 }
public:
 Example(std::size_t timeToSleep) : sleepTime(boost::posix_time::milliseconds(timeToSleep))
 {
  waiterMutex.lock();
  return;
 }
 ~Example(void)
 {
  waiterMutex.unlock();
  workThread.join();
  return;
 }
 bool Start(void)
 {
  bool result = false;
  if(workThread.joinable() == false)
  {
   waiterMutex.try_lock();
   workThread = boost::thread(boost::bind(&Example::WorkThread,this));
   result = true;
  }
  return result;
 }
 bool Stop(void)
 {
  bool result = false;
  waiterMutex.unlock();
  if(workThread.joinable() == true)
  {
   workThread.join();
  }
  return result;
 }
};
//-------------------------------------------------------------------------
int main(int argc,char** argv)
{
 std::string input;
 Example example(50000);
 while(true)
 {
  input.clear();
  std::cout << ":> ";
  std::cin >> input;
  if(!input.compare("quit"))
  {
   break;
  }
  else if(!input.compare("start"))
  {
   example.Start();
  }
  else if(!input.compare("stop"))
  {
   example.Stop();
  }
 }
 return 0;
}
Временная задержка выполнения потока обеспечивается ожиданием на mutex-е функцией timed_lock, а возможность немедленного завершения потока тем, что как сам mutex, так и его блокировка являются разделяемыми (shared_mutex и shared_lock). У разделяемого mutex-а есть две возможных блокировки: эксклюзивная, и разделяемая. Если на mutex-е эксклюзивная блокировка -- заблокировать его разделяемой будет невозможно. Обратное тоже верно. Но эксклюзивную блокировку может создавать только один поток, а разделяемую -- несколько. В представленном примере на mutex ставится эксклюзивная блокировка в конструкторе класса (строка 40), и перед попыткой пересоздать поток (строка 54). Поток (потоки), которому требуется выполнять в цикле некую функцию, пытается получить разделяемую блокировку на mutex (строка 28). Завершиться успехом это может только в случае, если основной поток отпустит эксклюзивную блокировку (строка 63, и вызов функции Stop в деструкторе класса). После чего требуется лишь дождаться завершения потока (потоков) функцией join.