Программирование

delay loaded dll – не всегда полезна

April 30th, 2009 Begemot

Дополнение к посту про то как вылечить проблему с OpenCV и wxWidgets путем использования отложенной загрузки библиотек,  сегодня нарвался на странную багу падает код загрузки каскада, для face detection

m_nested_cascade = (CvHaarClassifierCascade*)cvLoad( (const char*)path_nested_cascade.ToUTF8(), 0, 0, 0);

Оказалось падает из за использования delay loaded для OpenCV библиотек – вот такие странные бывают ситуации, все вылечилось путем добавления в лист отложенной загрузки только /delayload:highgui110d.dll  – а вот остальные пришлось оттуда убрать.

Странными иногда способами удается найти причину ошибку

Posted in Программирование | Tags:
Comments Off on delay loaded dll – не всегда полезна


OpenCV и wxWidgets лечим ошибку при запуске

April 16th, 2009 Begemot

На работе пришлось знакомится с OpenCV, в процессе прикручивания ее в проект на wxWidgets, возникла довольно неприятная  проблема. Как только начинаем пользовать функций из highgui – при старте программы выскакивает MessageBox с ошибкой “Cannot initialize OLE“, и на тулбаре появляется несколько дополнительных иконо – типа окна. 

Все проблема в том что OpenCV пытается самостоятельно инициализовать OLE механизм при инициализации библиотеки и это конфликтует с wxWidgets.  Этот баг описан в баг-трекерах и OpenCV и wxWidgets – но решения пока нет. Использовали последний официальный релиз, по слухам версия из SVN должна была помочь. Выкачал, собрал – не помогло. 

Спасла нас отложенная загрузка библиотек (delay loaded), это великая идея пришла в светлую голову T-rex’a, за что ему респект и уважуха. Все просто, тперь highgui.dll запускается и инициализируется позже чем wxWidgets и все счастливы. 

Как добавить delay loaded библиотеки в ручную и с использованием CMake

Вручную – свойства проекта -> Linker -> Input -> Delay Loaded DLLs

мы используем CMake – пришлось повозится, но в конечном итоге получилось через опции командной строки линкера

set(CMAKE_EXE_LINKER_FLAGS “/delayload:highgui110d.dll /delayload:cv110d.dll /delayload:cxcore110d.dll”)

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

Posted in Программирование
Comments Off on OpenCV и wxWidgets лечим ошибку при запуске


Используем png для иконок и храним ресурсы в архиве

December 24th, 2008 Begemot

Со временем я пришел к выводу что иконки лучше хранить в PNG, чем в XPM. Несомненый плюс это наличие альфа-канала. К тому же хранение иконок вне exe файла позволяет пользователю заменить используемые иконки на свои, что дает возможность правильно использовать для вашего приложения графику под LGPL лицензией (Где брать бесплатные иконки).

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

Я написал пару врапперов для удобной работы:

[sourcecode language=”cpp”]
// don’t forget call wxFileSystem::AddHandler(new wxZipFSHandler); in MyApp::OnInit()
wxImage BegUtils::GetImageFromZipResource(const wxString & path, long type/*=wxBITMAP_TYPE_ANY*/)
{
scoped_ptr fs(new wxFileSystem);
scoped_ptr file(fs->OpenFile(path));
if (!file) return wxNullImage;

wxInputStream* stream = file->GetStream();
wxImage image(*stream, type);

return image;
}

// Load a text resource from zip file
// don’t forget call wxFileSystem::AddHandler(new wxZipFSHandler); in MyApp::OnInit()
wxString BegUtils::GetUTF8TextFromZipResource(const wxString& path)
{
scoped_ptr fs(new wxFileSystem);
scoped_ptr file(fs->OpenFile(path));
if (!file) wxEmptyString;

wxInputStream* stream = file->GetStream();
size_t sz = stream->GetSize();
scoped_array buf(new char[sz]);
stream->Read((void*) buf.get(), sz);

wxString text = wxString::FromUTF8(buf.get(), sz);
if (text[0]==0xFEFF) text.erase(0, 1); // BOM
return text;
}
[/sourcecode]

 

Использование примерно такое

 

[sourcecode language=”cpp”]
wxString resPath=exePath+wxT(“resource.bin#zip:”);

#define MMB(x) BegUtils::GetImageFromZipResource(resPath+wxT(“icons\\”)+x+wxT(“.png”), wxBITMAP_TYPE_PNG)

AppendMenuItemWithImage(itemMenu, wxID_HELP, _T(“Help\tF1”), _T(“”), MMB(wxT(“help”)));
AppendMenuItemWithImage(itemMenu, wxID_ABOUT, _T(“About\tAlt+F1”), _T(“”), MMB(wxT(“information”)));
….
m_ToolBar->AddTool(wxID_EXIT, _T(“”), MMB(wxT(“exit”)), wxNullBitmap, wxITEM_NORMAL, _T(“Terminate Application (Alt+X)”), wxEmptyString);
….
#undef MMB
[/sourcecode]

AppendMenuItemWithImage можно взять в wxMenu с иконками и обход выделенных атемов в wxListCtrl

Пользуйтесь на здоровье.


Грабли с временными переменными

December 15th, 2008 Begemot

Я уже как-то писал про "мистику" . Вот сегодня еще наткнулся на интересные грабли. Задача такая есть юникодная строка надо конвертить ее в UTF8, получить указатель на то что получилось и дальше обработать. Если писать так

void foo(const char * data)

{

   int len=strlen(data);

   //работаем с указателем data

}

wxString str(wxT("sometext"));

foo(str.ToUTF8().data());

Все работает отлично. Если попытатся это реализовать прямо в коде

wxString str(wxT("sometext"));

const char *  cp=str.ToUTF8().data();

int len=strlen(cp);

//работаем с указателем data

То начинает бардак. Оно не работает, cp указывает на мусор, но если поставить брейкпоинт на 3 строчку и посмотреть в watch окне значение str.ToUTF8(), то после этого все хорошо, и cp указывает куда надо и длина правильно считается. Мистика:)

Если я правильно понимаю то дело во временности буфера возвращяемого ToUTF8(). В первом случаем он видимо уничтожается после выхода из функции, поэтому в теле функции указательно возвразщаемый data() валиден. Во втором wxCharBuffer уничтожается сразу же (?) и указатель кривой, но дебагер его как-то возвращяет в жизни…. – кто силен в теории с++ подскажите в чем дело грамотно:)

Вообщем что бы обойти это приходится писать вот такой код:

wxString str(wxT("sometext"));

const wxCharBuffer tcb=str.ToUTF8();

const char *  cp=tcb.data();

int len=strlen(cp);

//работаем с указателем data

Это работает!


Исключаем контрол из tab order’a

November 12th, 2008 Begemot

Минус в том что контрол надо субкласить, плюс в том что это работает:)

[sourcecode language=”cpp”]
h:
bool SetAllowSetFocusFromKbd(bool allow=true)
{
bool t=AllowSetFocusFromKbd;
AllowSetFocusFromKbd=allow;
return t;
};

bool GetAllowSetFocusFromKbd() const
{return AllowSetFocusFromKbd;};

virtual void SetFocusFromKbd();

private:
bool AllowSetFocusFromKbd;

cpp:
void CMyClass::SetFocusFromKbd()
{
if (AllowSetFocusFromKbd)
BaseClass::SetFocusFromKbd();
else
Navigate();
}
[/sourcecode]

 

Спасибо мудрецам c форума шадонета за наводку

Posted in Программирование | Tags: ,
Comments Off on Исключаем контрол из tab order’a


Как выборочно отключить шоткаты в меню. Часть вторая – фигвам.

October 1st, 2008 Begemot

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

Подход основывался на изменении tilte для wxMenu – удаления шотката при закрытии меню и добавления при открытии. Я еще тогда пытался изменить код на Enable\Disable – чтобы оставить текст тайтла в одном месте (легче менять если понадобится). Но это не сработало, разбиратся почему я не стал. Теперь знаю.

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

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

Вызывается OnOpenMenu, добавляет сокращения ко всем пунктам, которые мы вручную отключаем, а вот OnCloseMenu, в данном случае не вызывается.

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

Posted in Программирование | Tags: ,
Comments Off on Как выборочно отключить шоткаты в меню. Часть вторая – фигвам.


Как выборочно отключить шоткаты в меню

September 26th, 2008 Begemot

При создании меню в wxWidgets, в названии пункта меню можно указать шоткат, который будет использоватся для выбора этого пункта. Автоматическую обработку шоткатов wxWidgets берет на себя, что с одной стороны, конечно, удобно и приятно. Но с другой – иногда, подобная услужливость, явна лишняя.

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

Решение нашлось на форуме, собственно ответ тут. Добавляем обработчики для открытия\закрытия меню.

 
[sourcecode language=”cpp”]
EVT_MENU_OPEN(MainFrame::OnOpenMenu)
EVT_MENU_CLOSE(MainFrame::OnCloseMenu)

void MainFrame::OnOpenMenu(wxMenuEvent & event)
{
GetMenuBar()->FindItem(ID_COPYITEMTOCLIPBOARD)->SetItemLabel(_(“&Copy to Clipboard\tCtrl+C”));
event.Skip();
}

void MainFrame::OnCloseMenu(wxMenuEvent & event)
{
GetMenuBar()->FindItem(ID_COPYITEMTOCLIPBOARD)->SetItemLabel(_(“&Copy to Clipboard”));
GetMenuBar()->FindItem(ID_COPYITEMTOCLIPBOARD)->SetBitmaps(wxBitmap(copy_clipboard_xpm), wxNullBitmap);
event.Skip();
}
[/sourcecode]

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


Учимся вводить только цифры в wxTextCtrl

September 8th, 2008 Begemot

Поразительная фигня, казалось бы простая задача  и должно быть готовое решение. И правда есть, только поразительно кривое:(

Итак, задача – необходимо дать возможность пользователю указать число, например в настройках, количество записей  для отображения где-нибудь… Первое что приходит в голову – wxSlider, ну знаете такой контрол со стрелочками. Так как у меня диапазон от нуля до 10 000, а возможность задать шаг инкрементации для стрелочек я не нашел, и равен он 1 – то этот вариант, оказывается издевательством над юзером.

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

m_Count = new wxTextCtrl( this, ID_MAXCLIPTEXT, _T(""), wxDefaultPosition, wxSize(50, -1), 0, wxTextValidator(wxFILTER_NUMERIC, &count));

Я ожидал что контрол будет давать вводить в себя только цифры. Я ошибся, он также пропускал точку и запятую, что еще хоть как-то можно понять, хотя его и не просили. Но еще оказались разрешенными: ‘+’ и ‘–’. Также он спокойно пропускал все буквы кирилицы. Похоже что контрол отфильтровывал только пробел, спецсимволы и латиницу. Хотя даже это можно ввести туда пользуясь драг анд дропом или простой вставкой. Вообщем я разочаровался.

Попробывал задать более конкретно wxFILTER_INCLUDE_CHAR_LIST и разрешил только цифры. “голосуй, не голосуй, все равно … “ (с). Теже траблы со вставкой текста из буфера и …. с кирилицей. Прямо наваждение какое-то.

Решил писать сам, вот что получилось

[sourcecode language=”cpp”]
h:
void OnMaxClipTextUpdated( wxCommandEvent& event );

wxTextCtrl* m_Count;

unsigned long maxCount;
long maxInsertionPoint;
wxString maxCountStr;
bool maxSetManually;

cpp:
EVT_TEXT( ID_MAXCLIPTEXT, COPDatabase::OnMaxClipTextUpdated )

m_Count = new wxTextCtrl( this, ID_MAXCLIPTEXT, _T(“”), wxDefaultPosition, wxSize(50, -1), 0);
m_Count->SetMaxLength(4);

void COPDatabase::OnMaxClipTextUpdated( wxCommandEvent& event )
{
if (maxSetManually) return;
wxString str=m_Count->GetValue();

if (str.empty() || (str==wxT(” “))) // вторая проверка нужна для случая когда юзер выделяет все в контроле и жмет пробел
{
maxCount=0;
str=wxT(“0”);
maxInsertionPoint=1;
// or if you want to let empty string – replace last 2 string to these – maxCountStr=wxEmptyString; maxInsertionPoint=0;
}

if (str.ToULong(&maxCount))
{ // successfully converted to ulong, check range now
if (maxCount>10000) maxCount=10000;
else if (maxCount<0) maxCount=0; maxInsertionPoint=m_Count->GetInsertionPoint();
maxCountStr=wxString::Format(wxT(“%i”), maxCount); // we should do like this! иначе траблы с пробелами, нулями..
}

maxSetManually=true;
m_Count->SetValue(maxCountStr);
maxSetManually=false;
m_Count->SetInsertionPoint(maxInsertionPoint);
}
[/sourcecode]

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

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

Еще совет, лучше поставить ограничение на длину вводимых символов, типа m_Count->SetMaxLength(4); для верхней границы 10000 и т.д.

Пользуйтесь. Высказывайтесь.


Первые траблы с Chome и wxWidgets.

September 3rd, 2008 Begemot

Вот щеет, поставил Chome как дефолтный броузер, теперь мои wx-проги при попытке выполнить код перехода на веб страницу типа

wxLaunchDefaultBrowser(dHomeLink, wxBROWSER_NEW_WINDOW); 

запускают броузер и открывают страницу, но выдают ошибку

12:39:20: Can’t open registry key ‘HKCR\http\shell\open\DDEExec\topic’ (error 2: the system cannot find the file specified.)
12:39:20: Can’t read value of ‘HKCR\http\shell\open\DDEExec\topic’ (error 2: the system cannot find the file specified.)

Че за  новая напасть:(


Zip-уем данные на лету

September 2nd, 2008 Begemot

Встроенные удобства это приятно!:)

Пишу данные в базу, возникла мысль что архивирование может быть очень кстати, попробывал – действительно стало лучше. Арихивирование благодаря встроенным средствам wxWidgets делается легко и быстро. Просто пропускаем данные через wxZlibOutputStream который архивирует\распаковывает на лету.
[sourcecode language=”cpp”]
void SavecbContentToStream(wxMemoryOutputStream &memout, bool zlib/*=false*/) const
{
scoped_ptr dos;
scoped_ptr zlibstr;

if(zlib)
{
zlibstr.reset(new wxZlibOutputStream(memout, -1, wxZLIB_GZIP));
dos.reset(new wxDataOutputStream (*zlibstr));
}
else
dos.reset(new wxDataOutputStream (memout));

// write some data …
*dos <<(wxUint16)1 <<(wxUint16)cbContent.size(); // ... } [/sourcecode] Функция записывает данные в переданный буфер с опциональным архивированием их.