
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>wxWidgets</title>
	<atom:link href="https://wxwidgets.info/feed/" rel="self" type="application/rss+xml" />
	<link>https://wxwidgets.info</link>
	<description>Cross-Platform Programming</description>
	<lastBuildDate>Sat, 23 Nov 2013 19:53:46 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://wxwidgets.info/wordpress/wp-content/uploads/2023/06/icon-1-80x80.png</url>
	<title>wxWidgets</title>
	<link>https://wxwidgets.info</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets</title>
		<link>https://wxwidgets.info/razrabotka-krossplatformennyx-modulnyx-prilozhenij-na-c-s-bibliotekoj-wxwidgets/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=razrabotka-krossplatformennyx-modulnyx-prilozhenij-na-c-s-bibliotekoj-wxwidgets</link>
					<comments>https://wxwidgets.info/razrabotka-krossplatformennyx-modulnyx-prilozhenij-na-c-s-bibliotekoj-wxwidgets/#respond</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Sat, 23 Nov 2013 19:53:46 +0000</pubDate>
				<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[Tutorilas]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=616</guid>

					<description><![CDATA[<p>Введение Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень нравится и позволяет привести мысли в порядок. И все от того, что все это время был занят разработкой довольно интересного проекта. Но вот, есть возможность сейчас рассказать о наработках, которые появились за последнее время. Надеюсь, кому-то этот текст сильно упростит&#8230;</p>
<p>The post <a href="https://wxwidgets.info/razrabotka-krossplatformennyx-modulnyx-prilozhenij-na-c-s-bibliotekoj-wxwidgets/">Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<h3 id="vvedenie">Введение</h3>
<p>Уже долгое время не пишу статьи о разработке, хотя сам процесс написания мне очень нравится и позволяет привести мысли в порядок. И все от того, что все это время был занят разработкой довольно интересного проекта.</p>
<p>Но вот, есть возможность сейчас рассказать о наработках, которые появились за последнее время. Надеюсь, кому-то этот текст сильно упростит жизнь и даст толчок к покорению новых вершин.</p>
<p>В этот раз речь пойдет о создании кроссплатформенных приложений с плагинами на C++ с использованием библиотеки wxWidgets. Рассматриваться будут операционные системы Windows, Linux и OS X, как наиболее популярные.</p>
<p>Как обычно, первая часть будет обзорной, для того, чтобы снизить порог входа для читателей. Кому-то информация из первой части покажется очевидной (особенно то, что касается инструментария), но, все же, я считаю ее необходимой, ибо для новичков информация из первой части позволит с минимальными усилиями организовать процесс разработки.<br />
<span id="more-616"></span></p>
<h3 id="instrumentariy">Инструментарий</h3>
<h4 id="wxwidgets">wxWidgets</h4>
<p>Для начала нам понадобятся:</p>
<p>Библиотека wxWidgets в исходных кодах. Я использую наиболее новые версии из SVN. Они, конечно, не без багов, зато в них реализован функционал, которого обычно не хватает в официальных релизах.</p>
<p>Исходный код можно взять здесь: <a href="http://svn.wxwidgets.org/svn/wx/wxWidgets/trunk">http://svn.wxwidgets.org/svn/wx/wxWidgets/trunk</a></p>
<p>Более подробно о процессе сборки библиотеки можно почитать здесь: <a href="http://habrahabr.ru/post/123588/">http://habrahabr.ru/post/123588/</a></p>
<p>Разница в процессе сборки, по сравнению с указанной выше статьей заключается лишь в том, что нужно использовать конфигурацию <code>DLL Debug</code> и <code>DLL Release</code> вместо <code>Debug</code> и <code>Release</code>. К тому же, обязательно необходимо чтобы в настройках всех проектов, входящих в дистрибутив wxWidgets, в параметре <code>C/C++ -> Code Generation -> Runtime Library</code> были указаны значения <code>Multi-Threaded Debug DLL и Multi-Threaded DLL</code>. Именно с «DLL» в конце. В этом случае у нас wxWidgets будет собрана в виде динамических библиотек и с динамическим CRT.</p>
<p>При сборке конфигураций <code>DLL Debug</code> и <code>DLL Release</code> может быть такое что не все библиотеки соберутся с первого раза. Все это из-за проблем с указанием зависимостей. Если не собралось, запускаем сборку еще раз. Обычно 2-3 итераций достаточно для того, чтоб получить полный комплект динамических библиотек.</p>
<p>Напомню также, что для работы с wxWidgets необходимо наличие переменной окружения <code>%WXWIN%</code> (для Windows), которая указывает на папку с исходными кодами wxWidgets. Для Linux и OS X достаточно выполнить <code>configure && make && make install</code>.</p>
<p>Параметры для configure:</p>
<ul>
<li><b>Debug:</b> <code>configure --enable-shared --disable-static --enable-unicode \<br />
		--disable-compat28 --disable-final --enable-debug</code></li>
<li><b>Release:</b> <code>configure --enable-shared --disable-static --enable-unicode \<br />
		--disable-compat28 --enable-final --disable-debug</code></li>
</ul>
<h4 id="cmake">CMake</h4>
<p>Для того, чтобы облегчить работу по созданию файлов проектов для различных платформ на разных рабочих машинах с разными настройками, будем использовать систему генерации проектов CMake, о которой, кстати, есть несколько неплохих обзорных статей на Хабре, например вот:</p>
<ul>
<li><a href="http://habrahabr.ru/post/155467/">http://habrahabr.ru/post/155467/</a></li>
<li><a href="http://habrahabr.ru/post/178839/">http://habrahabr.ru/post/178839/</a></li>
<li><a href="http://habrahabr.ru/post/132313/">http://habrahabr.ru/post/132313/</a></li>
</ul>
<p>В общем, CMake – это инструмент, с помощью которого на разных машинах мы сможем генерировать файлы проектов Visual Studio (Windows), Makefile/CodeBlocks (Linux), Makefile/XCode (OS X) с правильно прописанными путями к исходным кодам и сторонним библиотекам, что позволит нам избавиться от довольно большого объема лишней работы по настройке сборки.</p>
<p>Скачать CMake можно здесь: <a href="http://www.cmake.org/cmake/resources/software.html">http://www.cmake.org/cmake/resources/software.html</a></p>
<p>Если вы собрали wxWidgets (Linux, OS X) с отладочной информацией, а потом хотите установить Release-версию, то надо сделать make uninstall для Debug-версии и вручную удалить файлы</p>
<ul>
<li>/usr/local/bin/wx-config</li>
<li>/usr/local/bin/wxrc</li>
</ul>
<p>Если указанные выше файлы не удалить вручную, то для Release-версии библиотеки будут использоваться настройки от Debug-версии. Приложение соберется, но не запустится.</p>
<p>Также надо иметь в виду тот факт, что если вы установили Debug-версию wxWidgets, то в Linux и OS X у вас, скорее всего, получится собрать только Debug-версию приложения. Это же касается и Release-версии. А все потому что CMake берет параметры компиляции и линковки из скрипта wx-config, который по умолчанию отдает параметры для одной текущей конфигурации. Или для Debug отдельно, или отдельно для Release.</p>
<h4 id="visual-c-windows">Visual C++ (Windows)</h4>
<p>Для сборки wxWidgets и нашего приложения из исходных кодов в Windows будем использовать Visual C++ 2012. Express редакция тоже подойдет. Это значит, что все средства разработки, включая IDE и компилятор, будут бесплатными.</p>
<p>Для тех, кто в танке, ссылка на бесплатный Visual C++ 2012: <a href="http://www.microsoft.com/visualstudio/rus/products/visual-studio-express-products">http://www.microsoft.com/visualstudio/rus/products/visual-studio-express-products</a></p>
<h4 id="dialogblocks">DialogBlocks</h4>
<p>Для создания интерфейса пользователя, дабы не писать все руками, рекомендую использовать приложение DialogBlocks. Таки-да, он платный, но есть бесплатная пробная версия, которой достаточно для создания несложных форм. Хотя опять же, никто не мешает писать все руками (кстати, это даже неплохо в воспитательных целях и явно положительно сказывается на понимании кода).</p>
<p>Скачать DialogBlocks можно здесь: <a href="http://www.anthemion.co.uk/dialogblocks/download.htm">http://www.anthemion.co.uk/dialogblocks/download.htm</a></p>
<h3 id="nachalo">Начало</h3>
<h4 id="struktura-papok">Структура папок</h4>
<p>Я понимаю, что на вкус и цвет фломастеры разные и навязывать свою структуру каталогов это дело неблагодарное, но за несколько лет работы мы в компании пришли к определенной структуре, которая неплохо зарекомендовала себя на довольно сложных проектах и которая довольно проста для понимания. Поэтому в данной статье будем использовать ее.</p>
<ul>
<li><b>build</b> – папка с общим CMake скриптом и shell-скриптами для генерирования проектов</li>
<li><b>build/bin/&lt;Configuration&gt;</b> &#8211; папка, куда компилятор складывает бинарные файлы</li>
<li><b>/include</b> – папка с общими заголовками (например, для precompiled headers)</li>
<li><b>/&lt;ProjectName&gt;</b> &#8211; папка с исходными кодами проекта из главного решения (может быть более одного проекта в решении, у каждого своя папка)</li>
<li><b>/&lt;ThirdParty&gt;</b> &#8211; папка, в которой лежат сторонние библиотеки (в виде исходников или собранные, каждая в своем подкаталоге)</li>
<li><b>/ThirdParty/build</b> – папка с общим CMake скриптом и shell-скриптами для генерирования проектов сторонних библиотек (если вы решите вынести их в отдельный solution)</li>
<li><b>/ThirdParty/&lt;LibName&gt;</b> &#8211; папка с исходными кодами сторонней библиотеки (их может быть более одной)</li>
<li><b>/&lt;ProjectName&gt;/&lt;OS-Name&gt;</b> &#8211; сюда CMake складывает файлы проектов для каждой ОС.</li>
</ul>
<h4 id="glavnyiy-cmakelist">Главный CMakeList</h4>
<p>Главный скрипт CMake содержит общие параметры и настройки для всех проектов, а также описание некоторых общих переменных.</p>
<p><b>build/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
cmake_minimum_required(VERSION 2.6.0)

# We will generate both Debug and Release project files at the same time
# for Windows and OS X
if(WIN32 OR APPLE)
	set(CMAKE_CONFIGURATION_TYPES &quot;Debug;Release&quot; CACHE STRING &quot;&quot; FORCE)
	set(LIB_SUFFIX &quot;&quot;)
endif(WIN32 OR APPLE)

# For Linux we will need to execute CMake twice in order to generate
# Debug and Release versions of Makefiles
if(UNIX AND NOT APPLE)
	set(LINUX ON)
	set(LIB_SUFFIX /${CMAKE_BUILD_TYPE})
endif(UNIX AND NOT APPLE)

set(PROJECT_NAME wxModularHost)
project(${PROJECT_NAME})

# If there are any additional CMake modules (e.g. module which searches
# for OpenCV or for DirectShow libs), then CMake should start searching
# for them in current folder
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})

if(APPLE)
	set(OS_BASE_NAME Mac)
	set(CMAKE_OSX_SYSROOT &quot;macosx10.6&quot;)
endif(APPLE)
if(LINUX)
	set(OS_BASE_NAME Linux)
endif(LINUX)
if(WIN32)
	set(OS_BASE_NAME Win)
endif(WIN32)

# Here we specify the list of wxWidgets libs which we will use in our project
set(wxWidgets_USE_LIBS base core adv aui net gl xml propgrid html)

# Here we specify that we need DLL version of wxWidgets libs and dynamic CRT
# This is a MUST for applications with plugins. Both app and DLL plugin MUST
# use the same instance of wxWidgets and the same event loop.
set(BUILD_SHARED_LIBS 1)

# Find wxWidgets library on current PC
# You should have %WXWIN%  environment variable which should point to the
# directory where wxWidgets source code is placed.
# wxWidgets libs MUST be compiled for both Debug and Release versions
find_package(wxWidgets REQUIRED)

# For some reason CMake generates wrong list of definitions.
# Each item should start with /D but it does not.
# We need to fix that manually
set(wxWidgets_DEFINITIONS_TEMP)
foreach(DEFINITION ${wxWidgets_DEFINITIONS})

	if(NOT ${DEFINITION} MATCHES &quot;/D.*&quot;)
		set(DEFINITION &quot;/D${DEFINITION}&quot;)
	endif()
	set(wxWidgets_DEFINITIONS_TEMP ${wxWidgets_DEFINITIONS_TEMP}
		${DEFINITION})
endforeach(${DEFINITION})
set(wxWidgets_DEFINITIONS ${wxWidgets_DEFINITIONS_TEMP})

# Here we add some definitions which prevent Visual Studio from
# generating tons of warnings about unsecure function calls.
# See http://msdn.microsoft.com/en-us/library/ttcz0bys.aspx
if(WIN32)
	set(wxWidgets_DEFINITIONS ${wxWidgets_DEFINITIONS};
		/D_CRT_SECURE_NO_DEPRECATE;
		/D_CRT_NONSTDC_NO_DEPRECATE;
		/D_UNICODE)
	set(CMAKE_CXX_FLAGS &quot;${CMAKE_CXX_FLAGS} /MP /wd4996&quot;)
endif(WIN32)

# Since we are going to use wxWidgets in all subrojects,
# it's OK to create the variable which will contain
# common preprocessor definitions. This variable will be
# used in subprojects.
set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};
	${wxWidgets_DEFINITIONS})

# Variable which points to root folder of our source code
set(PROJECT_ROOT_DIR ${PROJECT_SOURCE_DIR}/..)

# If any ThirdParty libraries are going to be
# used in our project then it would be better to put
# them into separate subfolder. We will create
# the variable which points to this subfolder.
set(THIRD_PARTY_DIR ${PROJECT_ROOT_DIR}/ThirdParty)

set(BASE_INCLUDE_DIRECTORIES ${PROJECT_ROOT_DIR}/include)

# Add wxWidgets include paths to the list of
# include directories for all projects.
include_directories(${wxWidgets_INCLUDE_DIRS})

set(CMAKE_CXX_FLAGS_DEBUG
	&quot;${CMAKE_CXX_FLAGS_DEBUG}
	/D__WXDEBUG__=1&quot; )

# Now we can include all our subprojects.
# CMake will generate project files for them
add_subdirectory (../wxModularHost
	../../wxModularHost/${OS_BASE_NAME}${LIB_SUFFIX})
</pre>
<h4 id="skriptyi-dlya-generirovaniya-proektov">Скрипты для генерирования проектов</h4>
<p>Для простоты использования CMake лучше использовать shell- или batch-скрипты. Это позволит немного сэкономить время на рутинных операциях типа вызова CMake и настройки переменных окружения.</p>
<h5 id="windows-cm-bat">Windows (cm.bat)</h5>
<p>Для удобства, лучше использовать раздельные batch-скрипты для создания проектов Visual Studio для x86 и x64, а также один общий скрипт, который будет определять, под какую платформу собираем приложение:</p>
<pre class="brush: bash; title: ; notranslate">
rem @echo off
IF &quot;%1&quot; == &quot;&quot; GOTO NO_PARAMS
IF &quot;%1&quot; == &quot;x86&quot; GOTO CMAKE_86
IF &quot;%1&quot; == &quot;86&quot;  GOTO CMAKE_86
IF &quot;%1&quot; == &quot;x64&quot; GOTO CMAKE_64
IF &quot;%1&quot; == &quot;64&quot;  GOTO CMAKE_64

ECHO %1
ECHO &quot;Nothing to do&quot;
GOTO End

:CMAKE_86
	ECHO &quot;Configuring for x86&quot;
	cm86.bat
	GOTO End
:CMAKE_64
	ECHO &quot;Configuring for x64&quot;
	cm64.bat
	GOTO End
:NO_PARAMS
	ECHO &quot;No parameters specified&quot;
	IF EXIST &quot;%ProgramW6432%&quot; GOTO CMAKE_64
	GOTO CMAKE_86
:End
</pre>
<h5 id="windows-cm86-bat">Windows (cm86.bat)</h5>
<pre class="brush: bash; title: ; notranslate">
rmdir /S /Q Win
mkdir Win
cd Win
cmake ../ -G &quot;Visual Studio 11&quot;
cd ..
</pre>
<h5 id="windows-cm64-bat">Windows (cm64.bat)</h5>
<pre class="brush: bash; title: ; notranslate">
rmdir /S /Q Win
mkdir Win
cd Win
cmake ../ -G &quot;Visual Studio 11 Win64&quot;
cd ..
</pre>
<h4 id="linux-cmlinux-sh">Linux (cmLinux.sh)</h4>
<pre class="brush: bash; title: ; notranslate">
#!/bin/bash
echo OS Type: $OSTYPE

# ----------------------------------
# build Debug configuration makefile
# ----------------------------------
echo building Debug configuration makefile
echo directory &quot;LinuxDebug&quot;
rm -dr &quot;LinuxDebug&quot;
mkdir &quot;LinuxDebug&quot;
cd &quot;LinuxDebug&quot;
cmake -G &quot;Unix Makefiles&quot; -DCMAKE_BUILD_TYPE:STRING=Debug ../
cd ..

# ----------------------------------
# build Release configuration makefile
# ----------------------------------
echo building Release configuration makefile
echo directory &quot;LinuxRelease&quot;
rm -dr &quot;LinuxRelease&quot;
mkdir &quot;LinuxRelease&quot;
cd &quot;LinuxRelease&quot;
cmake -G &quot;Unix Makefiles&quot; -DCMAKE_BUILD_TYPE:STRING=Release ../
cd ..
</pre>
<h3 id="minimalnoe-wxwidgets-prilozhenie-s-cmake">Минимальное wxWidgets-приложение с CMake</h3>
<p>Для начала работы нам нужен шаблон приложения, в который мы будем добавлять функционал. Создадим простое приложение, состоящее из класса приложения (например wxModularHostApp) и класса главной формы (например MainFrame).</p>
<p>Если использовать DialogBlocks, то, помимо пары файлов h/cpp для каждого класса, получим еще .rc файл с описанием ресурсов приложения.</p>
<p>Код приводить не буду. Пример можно взять из прошлых статей или из папки <code>%WXWIN%\samples\minimal</code></p>
<p>Теперь можно переходить к созданию CMake-скрипта.</p>
<p><b>wxModularHost/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
set(SRCS
	MainFrame.cpp
	wxModularHostApp.cpp)
set(HEADERS
	MainFrame.h
	wxModularHostApp.h)

set(INCLUDE_DIRECTORIES ${BASE_INCLUDE_DIRECTORIES})

if(WIN32)
	set(SRCS ${SRCS} wxModularHost.rc)
	set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};
		/D_USRDLL;
		/DwxUSE_NO_MANIFEST=1;
		/D__STDC_CONSTANT_MACROS)
endif(WIN32)

set(LIBS ${wxWidgets_LIBRARIES})

set(EXECUTABLE_NAME wxModularHost)

add_definitions(${PREPROCESSOR_DEFINITIONS})
include_directories(${INCLUDE_DIRECTORIES})

if(WIN32)
	set(EXECUTABLE_TYPE WIN32)
endif(WIN32)
if(APPLE)
	set(MACOSX_BUNDLE YES)
	set(EXECUTABLE_TYPE MACOSX_BUNDLE)
endif(APPLE)
if(LINUX)
	set(EXECUTABLE_TYPE &quot;&quot;)
endif(LINUX)

set(PROJECT_FILES ${SRCS} ${HFILES})
add_executable(${EXECUTABLE_NAME} ${EXECUTABLE_TYPE} ${PROJECT_FILES})

set(EXE_DIR bin)
set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${EXE_DIR}${LIB_SUFFIX})
set_target_properties(${EXECUTABLE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION})
target_link_libraries(${EXECUTABLE_NAME} ${LIBS})
</pre>
<h3 id="predvaritelno-otkompilirovannyie-zagolovki-precompiled-headers">Предварительно откомпилированные заголовки (Precompiled Headers)</h3>
<p>Для ускорения процесса компиляции, есть возможность использовать предварительно откомпилированные заголовки (<a href="http://en.wikipedia.org/wiki/Precompiled_header">http://en.wikipedia.org/wiki/Precompiled_header</a>).</p>
<p>Для реализации этой возможности нам понадобятся два файла:<br />
<b>include/stdwx.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#ifndef _STDWX_H_
#define _STDWX_H_

#if defined(WIN32) || defined(WINDOWS)
#include &lt;windows.h&gt;
#include &lt;winnt.h&gt;
#define PLUGIN_EXPORTED_API	WXEXPORT
#else
#define PLUGIN_EXPORTED_API	extern &quot;C&quot;
#endif
// SYSTEM INCLUDES
// For compilers that support precompilation, includes &quot;wx/wx.h&quot;.
#include &quot;wx/wxprec.h&quot;
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
#include &quot;wx/wx.h&quot;
#include &lt;wx/cmdline.h&gt;
#include &lt;wx/config.h&gt;
#include &lt;wx/defs.h&gt;
#include &lt;wx/dir.h&gt;
#include &lt;wx/display.h&gt;
#include &lt;wx/dynlib.h&gt;
#include &lt;wx/dynload.h&gt;
#include &lt;wx/fileconf.h&gt;
#include &lt;wx/filename.h&gt;
#include &lt;wx/frame.h&gt;
#include &lt;wx/glcanvas.h&gt;
#include &lt;wx/hashmap.h&gt;
#include &lt;wx/image.h&gt;
#include &lt;wx/imaglist.h&gt;
#include &lt;wx/intl.h&gt;
#include &lt;wx/list.h&gt;
#include &lt;wx/notebook.h&gt;
#include &lt;wx/stdpaths.h&gt;
#include &lt;wx/sstream.h&gt;
#include &lt;wx/thread.h&gt;
#include &lt;wx/treebook.h&gt;
#include &lt;wx/wfstream.h&gt;
#include &lt;wx/wupdlock.h&gt;
#include &lt;wx/textfile.h&gt;
#include &lt;wx/socket.h&gt;
#include &lt;wx/mimetype.h&gt;
#include &lt;wx/ipc.h&gt;

#endif
</pre>
<p><b>include/stdwx.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
</pre>
<p>Помимо файлов с исходным кодом C++ нам надо еще научить CMake добавлять в проект Visual Studio нужные правила для работы с предварительно откомпилированными заголовками. Для этого нам поможет специальный модуль. Не припомню, откуда он взялся, но вроде отсюда (<a href="http://public.kitware.com/Bug/file_download.php?file_id=901&amp;type=bug">http://public.kitware.com/Bug/file_download.php?file_id=901&amp;type=bug</a>). Исходный код CMake-модуля для поддержки предварительно компилируемых заголовков можно посмотреть здесь: <a href="https://github.com/T-Rex/wxModularApp/blob/master/build/PCHSupport.cmake">https://github.com/T-Rex/wxModularApp/blob/master/build/PCHSupport.cmake</a>.</p>
<p>Этот модуль надо включить в build/CmakeLists.txt таким образом:</p>
<p><b>build/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
cmake_minimum_required(VERSION 2.6.0)
include(PCHSupport.cmake)
...
</pre>
<p>После подключения предварительно откомпилированных заголовков в проект, первой строкой во всех .CPP файлах проекта должна быть строка</p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
</pre>
<h3 id="prosteyshiy-plagin-bez-gui">Простейший плагин без GUI</h3>
<h4 id="biblioteka-s-bazovyimi-klassami">Библиотека с базовыми классами</h4>
<p>Для разработки, собственно, плагина, и для того, чтобы приложение умело загружать плагины нужного типа, необходимо сделать подготовительную работу. Нужно создать библиотеку, которая будет содержать базовый абстрактный класс плагина, функционал которого мы должны будем реализовать в каждом конкретном плагине. Также наше основное приложение будет содержать указатели этого типа, которые были созданы внутри библиотек и ссылаются на конкретные реализации плагинов.</p>
<p>Т.е. в результате у нас должна подучиться библиотека, содержащая типы данных, используемые и в плагинах и в основном приложении.</p>
<p><b>wxNonGuiPluginBase/Declarations.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#ifndef _DECLARATIONS_H
#define _DECLARATIONS_H

#if defined(__WXMSW__)
#ifdef DEMO_PLUGIN_EXPORTS
#define DEMO_API __declspec(dllexport)
#else
#define DEMO_API __declspec(dllimport)
#endif
#else
#define DEMO_API
#endif

#endif // _DECLARATIONS_H
</pre>
<p><b>wxNonGuiPluginBase/wxNonGuiPluginBase.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

#include &quot;Declarations.h&quot;

class DEMO_API wxNonGuiPluginBase : public wxObject
{
	DECLARE_ABSTRACT_CLASS(wxNonGuiPluginBase)
public:
	wxNonGuiPluginBase();
	virtual ~wxNonGuiPluginBase();

	virtual int Work() = 0;
};

typedef wxNonGuiPluginBase * (*CreatePlugin_function)();
typedef void (*DeletePlugin_function)(wxNonGuiPluginBase * plugin);
</pre>
<p>Файл Declarations.h содержит определение макроса <code>DEMO_API</code>, который указывает, экспортируемый у нас класс <code>wxNonGuiPluginBase</code> или импортируемый. Делается это с помощью атрибутов <code>dllexport/dllimport</code> (см. <a href="http://msdn.microsoft.com/en-us/library/3y1sfaz2(v=vs.90).aspx)">http://msdn.microsoft.com/en-us/library/3y1sfaz2(v=vs.90).aspx)</a> в зависимости от наличия директивы препроцессора <code>DEMO_PLUGIN_EXPORTS</code>. При сборке библиотеки wxNonGuiPluginBase мы указываем <code>DEMO_PLUGIN_EXPORTS</code> в списке директив препроцессора, а при сборке плагинов, зависящих от библиотеки wxNonGuiPluginBase и при сборке основного приложения – не указываем. Таким образом для проекта wxNonGuiPluginBase значение <code>DEMO_API</code> будет содержать атрибут <code>dllexport</code>, а для всех остальных проектов – значение <code>dllimport</code>.</p>
<p><b>wxNonGuiPluginBase/wxNonGuiPluginBase.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
#include &quot;wxNonGuiPluginBase.h&quot;

IMPLEMENT_ABSTRACT_CLASS(wxNonGuiPluginBase, wxObject)

wxNonGuiPluginBase::wxNonGuiPluginBase()
{
}

wxNonGuiPluginBase::~wxNonGuiPluginBase()
{
}
</pre>
<p><b>wxNonGuiPluginBase/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
set (SRCS
	wxNonGuiPluginBase.cpp)
set (HEADERS
	Declarations.h
	wxNonGuiPluginBase.h)

set(LIBRARY_NAME wxNonGuiPluginBase)

if(WIN32)
	# Only for Windows:
	# we add additional preprocessor definitons
	set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};
		/D_USRDLL;/DDEMO_PLUGIN_EXPORTS;/D__STDC_CONSTANT_MACROS)
endif(WIN32)

# Add 2 files for precompiled headers
set(SRCS ${SRCS} ${HEADERS}
	${PROJECT_ROOT_DIR}/include/stdwx.h
	${PROJECT_ROOT_DIR}/include/stdwx.cpp)

# Set preprocessor definitions
add_definitions(${PREPROCESSOR_DEFINITIONS})
# Set include directories
include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES})
# Set library search paths
link_directories(${LINK_DIRECTORIES})
# Setup the project name and assign the source files for this project
add_library(${LIBRARY_NAME} SHARED ${SRCS})

#Setup the output folder
set(DLL_DIR bin)
set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX})
set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION})

# Set additional dependencies
target_link_libraries(${LIBRARY_NAME} ${wxWidgets_LIBRARIES})

# Setup precompiled headers
set_precompiled_header(${LIBRARY_NAME}
	${PROJECT_ROOT_DIR}/include/stdwx.h
	${PROJECT_ROOT_DIR}/include/stdwx.cpp)
</pre>
<p>Как было сказано ранее, макрос <code>PREPROCESSOR_DEFINITIONS</code> содержит декларацию макроса <code>DEMO_PLUGIN_EXPORTS</code>, который используется в файле Definitions.h</p>
<h4 id="pervyiy-plagin">Первый плагин</h4>
<p>В плагине нам надо сделать класс, производный от <code>wxNonGuiPluginBase</code>, реализовать в нем рабочий функционал, а также сделать экспортируемые функции для создания экземпляра класса и для его удаления. Эти функции будут вызываться основным приложением.</p>
<p><b>SampleNonGuiPlugin/SampleNonGuiPlugin.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

#include &lt;wxNonGuiPluginBase.h&gt;

class SampleNonGuiPlugin : public wxNonGuiPluginBase
{
	DECLARE_DYNAMIC_CLASS(SampleNonGuiPlugin)
public:
	SampleNonGuiPlugin();
	virtual ~SampleNonGuiPlugin();

	virtual int Work();
};
</pre>
<p><b>SampleNonGuiPlugin/SampleNonGuiPlugin.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
#include &quot;SampleNonGuiPlugin.h&quot;

IMPLEMENT_DYNAMIC_CLASS(SampleNonGuiPlugin, wxObject)

SampleNonGuiPlugin::SampleNonGuiPlugin()
{
}

SampleNonGuiPlugin::~SampleNonGuiPlugin()
{
}

int SampleNonGuiPlugin::Work()
{
	return 10;
}
</pre>
<p><b>SampleNonGuiPlugin/SampleNonGuiPlugin.def</b></p>
<pre class="brush: cpp; title: ; notranslate">
LIBRARY	&quot;SampleNonGuiPlugin&quot;

EXPORTS
	CreatePlugin=CreatePlugin
	DeletePlugin=DeletePlugin
</pre>
<p><b>SampleNonGuiPlugin/SampleNonGuiPluginExports.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
#include &lt;wxNonGuiPluginBase.h&gt;
#include &quot;SampleNonGuiPlugin.h&quot;

PLUGIN_EXPORTED_API wxNonGuiPluginBase * CreatePlugin()
{
	return new SampleNonGuiPlugin;
}

PLUGIN_EXPORTED_API void DeletePlugin(wxNonGuiPluginBase * plugin)
{
	wxDELETE(plugin);
}
</pre>
<p><b>SampleNonGuiPlugin/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
set (SRCS
	SampleNonGuiPlugin.cpp
	SampleNonGuiPluginExports.cpp)
set (HEADERS
	SampleNonGuiPlugin.h)

set(LIBRARY_NAME SampleNonGuiPlugin)

if(WIN32)
	set(SRCS ${SRCS} ${LIBRARY_NAME}.def)
	set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS)
	set(LINK_DIRECTORIES
		${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName))
	set(DEMO_LIBS wxNonGuiPluginBase.lib)
endif(WIN32)

set(SRCS ${SRCS} ${HEADERS}
	${PROJECT_ROOT_DIR}/include/stdwx.h
	${PROJECT_ROOT_DIR}/include/stdwx.cpp)

add_definitions(${PREPROCESSOR_DEFINITIONS})
include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}
	${PROJECT_ROOT_DIR}/wxNonGuiPluginBase)
link_directories(${LINK_DIRECTORIES})
add_library(${LIBRARY_NAME} SHARED ${SRCS})

set(DLL_DIR bin)
set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}/plugins)
set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION})

target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES})
add_dependencies(${LIBRARY_NAME} wxNonGuiPluginBase)
set_precompiled_header(${LIBRARY_NAME}
	${PROJECT_ROOT_DIR}/include/stdwx.h
	${PROJECT_ROOT_DIR}/include/stdwx.cpp)
</pre>
<p>DEF-файл включен в список файлов исходного кода не случайно. Без его использования, имена экспортируемых функций будут декорированными и приложение не сможет получить указатель на эти функции из DLL. Почитать на тему использования DEF-файлов и экспортирования функций из DLL можно здесь:</p>
<ul>
<li><a href="http://msdn.microsoft.com/en-us/library/office/bb687850.aspx">http://msdn.microsoft.com/en-us/library/office/bb687850.aspx</a></li>
<li><a href="http://msdn.microsoft.com/en-us/library/d91k01sh.aspx">http://msdn.microsoft.com/en-us/library/d91k01sh.aspx</a></li>
</ul>
<h3 id="modul-upravleniya-plaginami">Модуль управления плагинами</h3>
<p>Итак, на данный момент у нас есть хост-приложение и минимальный плагин. Теперь надо реализовать загрузку и использование плагина в приложении. В целях универсальности, лучше выделить код, который будет заниматься поиском, загрузкой и выгрузкой плагинов из памяти, в отдельный класс, а еще лучше – в отдельную библиотеку.</p>
<p>Вспомним еще раз реализацию наших плагинов:</p>
<ul>
<li>Плагин – это динамическая библиотека</li>
<li>В библиотеке есть экспортируемые функции <code>CreatePlugin()</code> и <code>DeletePlugin()</code></li>
<li>Весь функционал плагина реализуется в соответствующем классе внутри динамической библиотеки, объект этого класса возвращается функцией <code>CreatePlugin()</code></li>
<li>Класс внутри библиотеки реализует публичный интерфейс <code>wxNonGuiPluginBase</code>, о котором знает и приложение.</li>
<li>Библиотека должна быть загружена в память на протяжении всего времени жизни объект, который приложение получает из функции <code>CreatePlugin()</code></li>
<li>По завершении работы с плагином нам необходимо удалить объект из памяти (это делает функция <code>DeletePlugin()</code>) и выгрузить из памяти библиотеку.</li>
<li>Помимо загрузки и выгрузки данных из памяти, приложение должно еще уметь находить однотипные плагины в специально предназначенной для этого папке.</li>
</ul>
<p>Исходя из этих требований, можно прийти к таким выводам:</p>
<ul>
<li>Раз плагинов может быть более одного, то надо хранить список загруженных библиотек в памяти</li>
<li>Раз библиотек может быть более одной, то надо хранить список загруженных из библиотек объектов в памяти</li>
<li>Раз библиотека должна быть выгружена из памяти не ранее, чем удалится соответствующий рабочий объект, надо как-то обеспечить возможность отлеживать соответствия библиотеки этому объекту.</li>
</ul>
<p>Исходя из таких требований и выводов, реализовываем класс управления плагинами и контейнеры:<br />
<b>wxModularCore/wxModularCore.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

#include &lt;wxNonGuiPluginBase.h&gt;

// We need to know which DLL produced the specific plugin object.
WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*,
				wxPointerHash, wxPointerEqual,
				wxNonGuiPluginToDllDictionary);
// We also need to keep the list of loaded DLLs
WX_DECLARE_LIST(wxDynamicLibrary, wxDynamicLibraryList);
// And separate list of loaded plugins for faster access.
WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList);

class wxModularCoreSettings;

class wxModularCore
{
public:
	wxModularCore();
	virtual ~wxModularCore();

	virtual wxString GetPluginsPath(bool forceProgramPath) const;
	virtual wxString GetPluginExt();
	bool LoadPlugins(bool forceProgramPath);
	bool UnloadPlugins();

	const wxNonGuiPluginBaseList &amp; GetNonGuiPlugins() const;

	void Clear();
private:
	bool LoadNonGuiPlugins(const wxString &amp; pluginsDirectory);
	bool UnloadNonGuiPlugins();

	bool RegisterNonGuiPlugin(wxNonGuiPluginBase * plugin);
	bool UnRegisterNonGuiPlugin(wxNonGuiPluginBase * plugin);

	wxDynamicLibraryList m_DllList;
	wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll;
	wxNonGuiPluginBaseList m_NonGuiPlugins;

	wxModularCoreSettings * m_Settings;
};
</pre>
<p>Рассмотрим код подробно:</p>
<ul>
<li>В заголовочном файле есть декларация списка загруженных библиотек (<code>wxDynamicLibraryList</code>), списка загруженных из библиотеки объектов-плагинов (<code>wxNonGuiPluginBaseList</code>), а также хеш-таблицы, которая позволяет отследить соответствие библиотеки плагину (<code>wxNonGuiPluginToDllDictionary</code>)</li>
<li>Класс управления плагинами содержит метод, который возвращает путь к папке, в которой приложение будет искать плагины, а также метод, который возвращает расширение файлов плагинов (по умолчанию для Windows это .dll, а для Linux и OS X это .so)</li>
<li>Также класс содержит список библиотек, список объектов-плагинов и таблицу соответствий плагинов библиотекам.</li>
<li>Есть методы загрузки и выгрузки библиотек из памяти.</li>
<li>В классе есть поле <code>m_Settings</code>. Это указатель на объект, который будет хранить настройки системы (например, флаг, который определяет, где искать плагины и, возможно, данные или конфигурационные файлы для них, в папке с программой или в специальной папке настроек, путь к которой определяется системой). Более подробно класс настроек мы рассмотрим далее.</li>
</ul>
<p><b>wxModularCore/wxModularCore.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
#include &quot;wxModularCore.h&quot;
#include &quot;wxModularCoreSettings.h&quot;
#include &lt;wx/listimpl.cpp&gt;
WX_DEFINE_LIST(wxDynamicLibraryList);
WX_DEFINE_LIST(wxNonGuiPluginBaseList);

wxModularCore::wxModularCore()
	:m_Settings(new wxModularCoreSettings)
{
	// This will allow to delete all objects from this list automatically
	m_DllList.DeleteContents(true);
}

wxModularCore::~wxModularCore()
{
	Clear();
	wxDELETE(m_Settings);
}

void wxModularCore::Clear()
{
	UnloadPlugins();
	// TODO: Add the code which resets the object to initial state
}

bool wxModularCore::LoadPlugins(bool forceProgramPath)
{
	wxString pluginsRootDir = GetPluginsPath(forceProgramPath);

	wxFileName fn;
	fn.AssignDir(pluginsRootDir);
	wxLogDebug(wxT(&quot;%s&quot;), fn.GetFullPath().data());
	fn.AppendDir(wxT(&quot;plugins&quot;));
	wxLogDebug(wxT(&quot;%s&quot;), fn.GetFullPath().data());
	if (!fn.DirExists())
		return false;

	return LoadNonGuiPlugins(fn.GetFullPath());
}

bool wxModularCore::UnloadPlugins()
{
	return UnloadNonGuiPlugins();
}

bool wxModularCore::LoadNonGuiPlugins(const wxString &amp; pluginsDirectory)
{
	wxFileName fn;
	fn.AssignDir(pluginsDirectory);
	wxLogDebug(wxT(&quot;%s&quot;), fn.GetFullPath().data());
	fn.AppendDir(wxT(&quot;nongui&quot;));
	wxLogDebug(wxT(&quot;%s&quot;), fn.GetFullPath().data());
	if (!fn.DirExists())
		return false;

	if(!wxDirExists(fn.GetFullPath())) return false;
	wxString wildcard = wxString::Format(wxT(&quot;*.%s&quot;), GetPluginExt().GetData());
	wxArrayString pluginPaths;
	wxDir::GetAllFiles(fn.GetFullPath(), &amp;pluginPaths, wildcard);
	for(size_t i = 0; i &lt; pluginPaths.GetCount(); ++i)
	{
		wxString fileName = pluginPaths&#x5B;i];
		wxDynamicLibrary * dll = new wxDynamicLibrary(fileName);
		if (dll-&gt;IsLoaded())
		{
			wxDYNLIB_FUNCTION(CreatePlugin_function, CreatePlugin, *dll);
			if (pfnCreatePlugin)
			{
				wxNonGuiPluginBase* plugin = pfnCreatePlugin();
				RegisterNonGuiPlugin(plugin);
				m_DllList.Append(dll);
				m_MapNonGuiPluginsDll&#x5B;plugin] = dll;
			}
			else
				wxDELETE(dll);
		}
	}

	return true;
}

bool wxModularCore::UnloadNonGuiPlugins()
{
	bool result = true;
	wxNonGuiPluginBase * plugin = NULL;
	while (m_NonGuiPlugins.GetFirst() &amp;&amp; (plugin =
		m_NonGuiPlugins.GetFirst()-&gt;GetData()))
	{
		result &amp;= UnRegisterNonGuiPlugin(plugin);
	}
	return result;
}

wxString wxModularCore::GetPluginsPath(bool forceProgramPath) const
{
	wxString path;
	if (m_Settings-&gt;GetStoreInAppData() &amp;&amp; !forceProgramPath)
		path = wxStandardPaths::Get().GetConfigDir();
	else
		path = wxPathOnly(wxStandardPaths::Get().GetExecutablePath());
	return path;
}

wxString wxModularCore::GetPluginExt()
{
	return
#if defined(__WXMSW__)
		wxT(&quot;dll&quot;);
#else
		wxT(&quot;so&quot;);
#endif
}

bool wxModularCore::RegisterNonGuiPlugin(wxNonGuiPluginBase * plugin)
{
	m_NonGuiPlugins.Append(plugin);
	return true;
}

bool wxModularCore::UnRegisterNonGuiPlugin(wxNonGuiPluginBase * plugin)
{
	wxNonGuiPluginBaseList::compatibility_iterator it =
		m_NonGuiPlugins.Find(plugin);
	if (it == NULL)
		return false;

	do
	{
		wxDynamicLibrary * dll = m_MapNonGuiPluginsDll&#x5B;plugin];
		if (!dll) // Probably plugin was not loaded from dll
			break;

		wxDYNLIB_FUNCTION(DeletePlugin_function, DeletePlugin, *dll);
		if (pfnDeletePlugin)
		{
			pfnDeletePlugin(plugin);
			m_NonGuiPlugins.Erase(it);
			m_MapNonGuiPluginsDll.erase(plugin);
			return true;
		}
	} while (false);

	// If plugin is not loaded from DLL (e.g. embedded into executable)
	wxDELETE(plugin);
	m_NonGuiPlugins.Erase(it);

	return true;
}

const wxNonGuiPluginBaseList &amp; wxModularCore::GetNonGuiPlugins() const
{
	return m_NonGuiPlugins;
}
</pre>
<p>Есть смысл обратить внимание на метод <code>LoadNonGuiPlugins()</code>, в котором с помощью макроса <code>wxDYNLIB_FUNCTION</code> мы получаем указатель на функцию <code>CreatePlugin()</code>. Тип указателя <code>CreatePlugin_function</code> определен в wxNonGuiPluginBase.h.</p>
<p>Также есть смысл обратить внимание на метод <code>UnRegisterNonGuiPlugin()</code>, в котором происходит поиск плагина в таблице соответствий, если плагин найден то для него из найденной библиотеки вызывается функция <code>DeletePlugin()</code> и библиотека выгружается. Если плагин не найден в таблице (например, он реализован в приложении и мы его добавляли в список вручную), то он просто удаляется из памяти.</p>
<p><b>wxModularCore/wxModularCoreSettings.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

class wxModularCoreSettings
{
public:
	wxModularCoreSettings();
	wxModularCoreSettings(const wxModularCoreSettings &amp; settings);
	wxModularCoreSettings &amp; operator = (const wxModularCoreSettings &amp; settings);
	virtual ~wxModularCoreSettings();

	void SetStoreInAppData(const bool &amp; val);
	bool GetStoreInAppData() const;
protected:
	virtual void CopyFrom(const wxModularCoreSettings &amp; settings);
private:
	bool m_bStoreInAppData; // Should we store data in Application Data folder or in .exe folder
};
</pre>
<p><b>wxModularCore/wxModularCoreSettings.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
#include &quot;wxModularCoreSettings.h&quot;

wxModularCoreSettings::wxModularCoreSettings()
	: m_bStoreInAppData(false)
{
}

wxModularCoreSettings::wxModularCoreSettings(const wxModularCoreSettings &amp; settings)
{
	CopyFrom(settings);
}

wxModularCoreSettings &amp; wxModularCoreSettings::operator = (const wxModularCoreSettings &amp; settings)
{
	if (this != &amp;settings)
	{
		CopyFrom(settings);
	}
	return *this;
}

wxModularCoreSettings::~wxModularCoreSettings()
{

}

void wxModularCoreSettings::CopyFrom(const wxModularCoreSettings &amp; settings)
{
	m_bStoreInAppData = settings.m_bStoreInAppData;
}

void wxModularCoreSettings::SetStoreInAppData(const bool &amp; value)
{
	m_bStoreInAppData = value;
}

bool wxModularCoreSettings::GetStoreInAppData() const
{
	return m_bStoreInAppData;
}
</pre>
<p><b>wxModularCore/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
set (SRCS
	wxModularCore.cpp
	wxModularCoreSettings.cpp)
set (HEADERS
	wxModularCore.h
	wxModularCoreSettings.h)

set(LIBRARY_NAME wxModularCore)

if(WIN32)
	set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D__STDC_CONSTANT_MACROS)
	set(LINK_DIRECTORIES
		${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName))
	set(DEMO_LIBS wxNonGuiPluginBase.lib)
endif(WIN32)

set(SRCS ${SRCS} ${HEADERS}
	${PROJECT_ROOT_DIR}/include/stdwx.h
	${PROJECT_ROOT_DIR}/include/stdwx.cpp)

add_definitions(${PREPROCESSOR_DEFINITIONS})

include_directories(${INCLUDE_DIRECTORIES} ${BASE_INCLUDE_DIRECTORIES}
	${PROJECT_ROOT_DIR}/wxNonGuiPluginBase)

link_directories(${LINK_DIRECTORIES})

add_library(${LIBRARY_NAME} STATIC ${SRCS})

set(DLL_DIR bin)
set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR})
set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION})

target_link_libraries(${LIBRARY_NAME} ${DEMO_LIBS} ${wxWidgets_LIBRARIES})

add_dependencies(${LIBRARY_NAME} wxNonGuiPluginBase)

set_precompiled_header(${LIBRARY_NAME} ${PROJECT_ROOT_DIR}/include/stdwx.h ${PROJECT_ROOT_DIR}/include/stdwx.cpp)
</pre>
<p>И еще надо не забыть включить путь к проекту wxModularCore в основной CMakeLists.txt:</p>
<p><b>build/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
...
add_subdirectory (../wxModularCore
	../../wxModularCore/${OS_BASE_NAME}${LIB_SUFFIX})
...
</pre>
<h3 id="ispolzovanie-plaginov-bez-gui-v-prilozhenii">Использование плагинов без GUI в приложении</h3>
<p>Раз класс, управляющий плагинами, у нас уже готов, то можно начать им пользоваться в приложении.</p>
<p>Для начала поле-указатель на <code>wxModularCore</code> в класс приложения:</p>
<p><b>wxModularHost/wxModularHostApp.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
...
class wxModularHostApp: public wxApp
{
	void TestNonGuiPlugins();
...
	wxModularCore * m_PluginManager;
...
};
</pre>
<p><b>wxModularHost/wxModularHostApp.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
void wxModularHostApp::Init()
{
////@begin wxModularHostApp member initialisation
	m_PluginManager = new wxModularCore;
////@end wxModularHostApp member initialisation
}
</pre>
<p>И вот таким образом мы будем вызывать метод загрузки плагинов и пользоваться самими плагинами:</p>
<p><b>wxModularHost/wxModularHostApp.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
bool wxModularHostApp::OnInit()
{
...
	TestNonGuiPlugins();

	MainFrame* mainWindow = new MainFrame( NULL );
	mainWindow-&gt;Show(true);

    return true;
}

/*
 * Cleanup for wxModularHostApp
 */

int wxModularHostApp::OnExit()
{
	wxDELETE(m_PluginManager);
////@begin wxModularHostApp cleanup
	return wxApp::OnExit();
////@end wxModularHostApp cleanup
}

void wxModularHostApp::TestNonGuiPlugins()
{
	if(m_PluginManager)
	{
		if(m_PluginManager-&gt;LoadPlugins(true))
		{
			for(wxNonGuiPluginBaseList::Node * node =
				m_PluginManager-&gt;GetNonGuiPlugins().GetFirst(); node; node = node-&gt;GetNext())
			{
				wxNonGuiPluginBase * plugin = node-&gt;GetData();
				if(plugin)
				{
					wxLogDebug(wxT(&quot;Non-GUI plugin returns %i&quot;), plugin-&gt;Work());
				}
			}
		}
	}
}
</pre>
<p>В методе <code>TestNonGuiPlugins()</code> мы сначала вызываем метод <code>LoadPlugins()</code> из <code>wxModularCore</code>, если он отработал корректно, то проходимся по списку плагинов и для каждого элемента списка вызываем метод <code>Work()</code> (напомню, он задекларирован в проекте wxNonGuiPluginBase, а фактически имеет разную реализацию для каждой из загруженных библиотек).</p>
<h3 id="prosteyshiy-gui-plagin">Простейший GUI-плагин</h3>
<p>Как создавать модули, содержащие только логику, разобрались. Теперь рассмотрим пример модуля, который кмеет создавать окно:</p>
<p><b>wxGuiPluginBase/wxGuiPluginBase.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

#include &quot;Declarations.h&quot;

class DEMO_API wxGuiPluginBase : public wxObject
{
	DECLARE_ABSTRACT_CLASS(wxGuiPluginBase)
public:
	wxGuiPluginBase();
	virtual ~wxGuiPluginBase();

	virtual wxString GetName() const = 0;
	virtual wxString GetId() const = 0;
	virtual wxWindow * CreatePanel(wxWindow * parent) = 0;
};

typedef wxGuiPluginBase * (*CreateGuiPlugin_function)();
typedef void (*DeleteGuiPlugin_function)(wxGuiPluginBase * plugin);
</pre>
<p>Публичные виртуальные методы:</p>
<ul>
<li><code>GetName()</code> – возвращает название модуля</li>
<li><code>GetId()</code> – возвращает уникальный идентификатор модуля (можно использовать GUID для этого, в Visual Studio для этих целей есть специальная утилита. См. меню <code>Tools -> Create GUID</code>)</li>
<li><code>CreatePanel()</code> – создает элемент управления (для демонстрации нас устроит любой контрол) и возвращает указатель на него.</li>
</ul>
<p>Реализация плагина на основе этого интерфейса:</p>
<p><b>SampleGuiPlugin1/SampleGuiPlugin1.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

#include &lt;wxGuiPluginBase.h&gt;

class SampleGuiPlugin1 : public wxGuiPluginBase
{
	DECLARE_DYNAMIC_CLASS(SampleGuiPlugin1)
public:
	SampleGuiPlugin1();
	virtual ~SampleGuiPlugin1();

	virtual wxString GetName() const;
	virtual wxString GetId() const;
	virtual wxWindow * CreatePanel(wxWindow * parent);
};
</pre>
<p><b>SampleGuiPlugin1/SampleGuiPlugin1.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
#include &quot;SampleGuiPlugin1.h&quot;

IMPLEMENT_DYNAMIC_CLASS(SampleGuiPlugin1, wxObject)

SampleGuiPlugin1::SampleGuiPlugin1()
{
}

SampleGuiPlugin1::~SampleGuiPlugin1()
{
}

wxString SampleGuiPlugin1::GetName() const
{
	return _(&quot;GUI Plugin 1&quot;);
}

wxString SampleGuiPlugin1::GetId() const
{
	return wxT(&quot;{4E97DF66-5FBB-4719-AF17-76C1C82D3FE1}&quot;);
}

wxWindow * SampleGuiPlugin1::CreatePanel(wxWindow * parent)
{
	wxWindow * result= new wxPanel(parent, wxID_ANY);
	result-&gt;SetBackgroundColour(*wxRED);
	return result;
}
</pre>
<p>CMakeLists.txt для этого плагина почти аналогичен тому, который мы написали для плагина без GUI. Отличия будут только в названии проекта и в списке входящих в проект файлов.</p>
<h3 id="refaktoring-modulya-upravleniya-plaginami">Рефакторинг модуля управления плагинами</h3>
<p>На данный момент у нас есть два типа плагинов. Для плагинов без GUI в классе управления плагинами есть специализированный метод для загрузки библиотек, регистрации плагинов, отключения плагинов. С таким подходом нам нужно будет дублировать все эти методы для каждого типа плагинов. И есть таковых у нас будет 5-10, то класс неоправданно разрастется в размерах. Поэтому методы <code>LoadXXXPlugins()</code>, <code>UnloadXXXPlugins()</code>, <code>RegisterXXXPlugin()</code>, <code>UnRegisterXXXPlugin()</code> было решено сделать шаблонными, списки и хеш-таблицы вынести в отдельный класс-наследник класса <code>wxModularCore</code>, который будет содержать код, специфичный для нашего приложения.<br />
<b>wxModularCore/wxModularCore.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

// We need to keep the list of loaded DLLs
WX_DECLARE_LIST(wxDynamicLibrary, wxDynamicLibraryList);

class wxModularCoreSettings;

class wxModularCore
{
public:
	wxModularCore();
	virtual ~wxModularCore();

	virtual wxString GetPluginsPath(bool forceProgramPath) const;
	virtual wxString GetPluginExt();

	virtual bool LoadAllPlugins(bool forceProgramPath) = 0;
	virtual bool UnloadAllPlugins() = 0;
	virtual void Clear();
protected:
	wxDynamicLibraryList m_DllList;
	wxModularCoreSettings * m_Settings;

	template&lt;typename PluginType,
		typename PluginListType&gt;
		bool RegisterPlugin(PluginType * plugin,
		PluginListType &amp; list)
	{
		list.Append(plugin);
		return true;
	}

	template&lt;typename PluginType,
		typename PluginListType,
		typename PluginToDllDictionaryType,
		typename DeletePluginFunctionType&gt;
		bool UnRegisterPlugin(
			PluginType * plugin,
			PluginListType &amp; container,
			PluginToDllDictionaryType &amp; pluginMap)
	{
		typename PluginListType::compatibility_iterator it =
			container.Find(plugin);
		if (it == NULL)
			return false;

		do
		{
			wxDynamicLibrary * dll = (wxDynamicLibrary *)pluginMap&#x5B;plugin];
			if (!dll) // Probably plugin was not loaded from dll
				break;

			wxDYNLIB_FUNCTION(DeletePluginFunctionType,
				DeletePlugin, *dll);
			if (pfnDeletePlugin)
			{
				pfnDeletePlugin(plugin);
				container.Erase(it);
				pluginMap.erase(plugin);
				return true;
			}
		} while (false);

		// If plugin is not loaded from DLL (e.g. embedded into executable)
		wxDELETE(plugin);
		container.Erase(it);

		return true;
	}

	template&lt;typename PluginType,
		typename PluginListType,
		typename PluginToDllDictionaryType,
		typename DeletePluginFunctionType&gt;
	bool UnloadPlugins(PluginListType &amp; list,
		PluginToDllDictionaryType &amp; pluginDictoonary)
	{
		bool result = true;
		PluginType * plugin = NULL;
		while (list.GetFirst() &amp;&amp; (plugin =
			list.GetFirst()-&gt;GetData()))
		{
			result &amp;= UnRegisterPlugin&lt;PluginType,
				PluginListType,
				PluginToDllDictionaryType,
				DeletePluginFunctionType&gt;(plugin,
					list, pluginDictoonary);
		}
		return result;
	}

	template &lt;typename PluginType,
		typename PluginListType,
		typename PluginToDllDictionaryType,
		typename CreatePluginFunctionType&gt;
	bool LoadPlugins(const wxString &amp; pluginsDirectory,
		PluginListType &amp; list,
		PluginToDllDictionaryType &amp; pluginDictionary,
		const wxString &amp; subFolder)
	{
		wxFileName fn;
		fn.AssignDir(pluginsDirectory);
		wxLogDebug(wxT(&quot;%s&quot;), fn.GetFullPath().data());
		fn.AppendDir(subFolder);
		wxLogDebug(wxT(&quot;%s&quot;), fn.GetFullPath().data());
		if (!fn.DirExists())
			return false;

		if(!wxDirExists(fn.GetFullPath())) return false;
		wxString wildcard = wxString::Format(wxT(&quot;*.%s&quot;),
			GetPluginExt().GetData());
		wxArrayString pluginPaths;
		wxDir::GetAllFiles(fn.GetFullPath(),
			&amp;pluginPaths, wildcard);
		for(size_t i = 0; i &lt; pluginPaths.GetCount(); ++i)
		{
			wxString fileName = pluginPaths&#x5B;i];
			wxDynamicLibrary * dll = new wxDynamicLibrary(fileName);
			if (dll-&gt;IsLoaded())
			{
				wxDYNLIB_FUNCTION(CreatePluginFunctionType,
					CreatePlugin, *dll);
				if (pfnCreatePlugin)
				{
					PluginType * plugin = pfnCreatePlugin();
					RegisterPlugin(plugin, list);
					m_DllList.Append(dll);
					pluginDictionary&#x5B;plugin] = dll;
				}
				else
					wxDELETE(dll);
			}
		}
		return true;
	}

};
</pre>
<p><b>wxModularHost/SampleModularCore.h</b></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

#include &lt;wxModularCore.h&gt;
#include &lt;wxNonGuiPluginBase.h&gt;
#include &lt;wxGuiPluginBase.h&gt;

// We need to know which DLL produced the specific plugin object.
WX_DECLARE_HASH_MAP(wxNonGuiPluginBase*, wxDynamicLibrary*,
					wxPointerHash, wxPointerEqual,
					wxNonGuiPluginToDllDictionary);
WX_DECLARE_HASH_MAP(wxGuiPluginBase*, wxDynamicLibrary*,
					wxPointerHash, wxPointerEqual,
					wxGuiPluginToDllDictionary);
// And separate list of loaded plugins for faster access.
WX_DECLARE_LIST(wxNonGuiPluginBase, wxNonGuiPluginBaseList);
WX_DECLARE_LIST(wxGuiPluginBase, wxGuiPluginBaseList);

class SampleModularCore : public wxModularCore
{
public:
	virtual ~SampleModularCore();
	virtual bool LoadAllPlugins(bool forceProgramPath);
	virtual bool UnloadAllPlugins();

	const wxNonGuiPluginBaseList &amp; GetNonGuiPlugins() const;
	const wxGuiPluginBaseList &amp; GetGuiPlugins() const;
private:
	wxNonGuiPluginToDllDictionary m_MapNonGuiPluginsDll;
	wxNonGuiPluginBaseList m_NonGuiPlugins;
	wxGuiPluginToDllDictionary m_MapGuiPluginsDll;
	wxGuiPluginBaseList m_GuiPlugins;
};
</pre>
<p><b>wxModularHost/SampleModularCore.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;stdwx.h&quot;
#include &quot;SampleModularCore.h&quot;
#include &lt;wx/listimpl.cpp&gt;

WX_DEFINE_LIST(wxNonGuiPluginBaseList);
WX_DEFINE_LIST(wxGuiPluginBaseList);

SampleModularCore::~SampleModularCore()
{
	Clear();
}

bool SampleModularCore::LoadAllPlugins(bool forceProgramPath)
{
	wxString pluginsRootDir = GetPluginsPath(forceProgramPath);
	bool result = true;
	result &amp;= LoadPlugins&lt;wxNonGuiPluginBase,
		wxNonGuiPluginBaseList,
		wxNonGuiPluginToDllDictionary,
		CreatePlugin_function&gt;(pluginsRootDir,
		m_NonGuiPlugins,
		m_MapNonGuiPluginsDll,
		wxT(&quot;nongui&quot;));
	result &amp;= LoadPlugins&lt;wxGuiPluginBase,
		wxGuiPluginBaseList,
		wxGuiPluginToDllDictionary,
		CreateGuiPlugin_function&gt;(pluginsRootDir,
		m_GuiPlugins,
		m_MapGuiPluginsDll,
		wxT(&quot;gui&quot;));
	// You can implement other logic which takes in account
	// the result of LoadPlugins() calls
	return true;
}

bool SampleModularCore::UnloadAllPlugins()
{
	return
		UnloadPlugins&lt;wxNonGuiPluginBase,
			wxNonGuiPluginBaseList,
			wxNonGuiPluginToDllDictionary,
			DeletePlugin_function&gt;(m_NonGuiPlugins,
			m_MapNonGuiPluginsDll) &amp;&amp;
		UnloadPlugins&lt;wxGuiPluginBase,
			wxGuiPluginBaseList,
			wxGuiPluginToDllDictionary,
			DeleteGuiPlugin_function&gt;(m_GuiPlugins,
			m_MapGuiPluginsDll);
}

const wxNonGuiPluginBaseList &amp; SampleModularCore::GetNonGuiPlugins() const
{
	return m_NonGuiPlugins;
}

const wxGuiPluginBaseList &amp; SampleModularCore::GetGuiPlugins() const
{
	return m_GuiPlugins;
}
</pre>
<p>После реализации шаблонных методов, добавление поддержки GUI-плагинов заняло совсем немного кода.</p>
<h3 id="ispolzovanie-gui-plaginov-v-prilozhenii">Использование GUI-плагинов в приложении</h3>
<p>В приложении у нас есть главная форма с менеджером Docking-окон и wxAuiNotebook в качестве центральной панели. Рассмотрим как можно добавить контролы из плагинов в этот wxAuiNotebook:<br />
<b>wxModularHost/MainFrame.cpp</b></p>
<pre class="brush: cpp; title: ; notranslate">
void MainFrame::AddPagesFromGuiPlugins()
{
	SampleModularCore * pluginManager = wxGetApp().GetPluginManager();
	for(wxGuiPluginBaseList::Node * node = pluginManager-&gt;GetGuiPlugins().GetFirst();
		node; node = node-&gt;GetNext())
	{
		wxGuiPluginBase * plugin = node-&gt;GetData();
		if(plugin)
		{
			wxWindow * page = plugin-&gt;CreatePanel(m_Notebook);
			if(page)
			{
				m_Notebook-&gt;AddPage(page, plugin-&gt;GetName());
			}
		}
	}
}
</pre>
<p>В результате получим такое окно с вкладками:</p>
<p><a class="lightbox" href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2013/11/modular-app-screenshot.png"><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-617" alt="modular-app-screenshot" src="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2013/11/modular-app-screenshot.png" width="600" height="450" srcset="https://wxwidgets.info/wordpress/wp-content/uploads/2013/11/modular-app-screenshot.png 600w, https://wxwidgets.info/wordpress/wp-content/uploads/2013/11/modular-app-screenshot-300x225.png 300w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Заголовки вкладок берутся из метода <code>GetName()</code> каждого плагина, сами же вкладки создаются методом <code>CreatePanel()</code> плагина.</p>
<h3 id="dorabotki-cmake-skriptov-dlya-linux">Доработки CMake-скриптов для Linux</h3>
<p>В Windows для указания папки, в которую будут собираться динамические библиотеки, мы указывали с помощью настройки <code>RUNTIME_OUTPUT_DIRECTORY</code>. В Linux, т.к. плагин – это динамическая библиотека (именно библиотека), используется настройка <code>LIBRARY_OUTPUT_DIRECTORY</code>. Но здесь мы сталкиваемся с проблемой: если собирать библиотеки прямо внутрь папки bin, то линкер не будет находить эту библиотеку при сборке зависимых проектов. Для этих целей нужно добавить скрипт, который будет отрабатывать после сборки библиотеки и копировать ее в нужное место внутрь папки bin. Сделать это нужно будет для всех динамических библиотек (и для базовых и для плагинов):</p>
<p><b>SampleGuiPlugin2/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
...
set(DLL_DIR bin)
if(LINUX)
	set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}${LIB_SUFFIX}/plugins/nongui)
else(LINUX)
	set(TARGET_LOCATION ${PROJECT_SOURCE_DIR}/${DLL_DIR}/${CMAKE_CFG_INTDIR}/plugins/nongui)
	get_target_property(RESULT_FULL_PATH ${LIBRARY_NAME} LOCATION)
	get_filename_component(RESULT_FILE_NAME ${RESULT_FULL_PATH} NAME)
endif(LINUX)
set_target_properties(${LIBRARY_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${TARGET_LOCATION})
...
if(LINUX)
	add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD
		COMMAND ${CMAKE_COMMAND} -E make_directory ${TARGET_LOCATION}
		COMMAND ${CMAKE_COMMAND} -E copy $&lt;TARGET_FILE:${LIBRARY_NAME}&gt;
			${TARGET_LOCATION}/${RESULT_FILE_NAME}
	)
endif(LINUX)
</pre>
<p>Для всех плагинов в Linux мы также должны указать список зависимостей:</p>
<p><b>SampleGuiPlugin2/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
...
if(WIN32)
	set(SRCS ${SRCS} ${LIBRARY_NAME}.def)
	set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS)
	set(LINK_DIRECTORIES
		${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName))
	set(DEMO_LIBS wxNonGuiPluginBase.lib)
endif(WIN32)
if(LINUX)
	set(DEMO_LIBS wxNonGuiPluginBase)
endif(LINUX)
...
</pre>
<p>Вроде все хорошо, проект собирается. Но при попытке запустить приложение мы обнаружим что динамические библиотеки не загружаются (это может стать неприятной неожиданностью в случае переноса на другую машину уже собранного приложения).<br />
А все потому, что при сборке внутрь библиотеки прописываются ссылки на зависимости с полными путями. Так, например, для каждого плагина будет указан полный путь к библиотеке с базовыми классами, которой на другой рабочей машине не будет. Проверить это можно с помощью утилиты ldd:</p>
<pre class="brush: bash; title: ; notranslate">
ldd libSampleGuiPlugin2.so | grep wxSampleGuiPluginBase
</pre>
<p>Для того, чтобы избавиться от полных путей, в CMake надо указать опцию <code>RPATH</code> для библиотек, которые ссылаются на другие динамические библиотеки из нашего решения:</p>
<p><b>SampleGuiPlugin2/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
if(WIN32)
	set(SRCS ${SRCS} ${LIBRARY_NAME}.def)
	set(PREPROCESSOR_DEFINITIONS ${PREPROCESSOR_DEFINITIONS};/D_USRDLL;/D__STDC_CONSTANT_MACROS)
	set(LINK_DIRECTORIES
		${PROJECT_ROOT_DIR}/wxNonGuiPluginBase/${OS_BASE_NAME}${LIB_SUFFIX}/$(ConfigurationName))
	set(DEMO_LIBS wxNonGuiPluginBase.lib)
endif(WIN32)
if(LINUX)
	set(DEMO_LIBS wxNonGuiPluginBase)
	SET(CMAKE_SKIP_BUILD_RPATH  FALSE)
	SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
	SET(CMAKE_INSTALL_RPATH &quot;.:./../../&quot;)
endif(LINUX)
</pre>
<p>Т.к. плагин находится в подкаталоге plugins/gui, то бибиотеку wxGuiPluginBase надо искать на два уровня выше, что и указано в CMakeLists.txt</p>
<h3 id="dorabotka-cmake-skriptov-dlya-os-x">Доработка CMake-скриптов для OS X</h3>
<p>Так же, как и в Linux, в OS X у нас появляется проблема с загрузкой зависимостей у плагинов. В OS X для исправления путей к динамическим библиотекам, можно использовать утилиту install_name_tool.<br />
Допишем код в CMakeLists.txt, который заменяет пути к библиотекам на относительные:<br />
<b>SampleGuiPlugin2/CMakeLists.txt</b></p>
<pre class="brush: cpp; title: ; notranslate">
if(APPLE)
	FOREACH(DEP_LIB ${DEMO_LIBS})
		get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE)
		set(LIBNAME_FULL &quot;${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib&quot;)
                add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD
                	COMMAND install_name_tool -change &quot;${LIBNAME_FULL}&quot;
						&quot;@executable_path/../Frameworks/lib${DEP_LIB}.dylib&quot;
						$&lt;TARGET_FILE:${LIBRARY_NAME}&gt;)
        ENDFOREACH(DEP_LIB)
endif(APPLE)
</pre>
<p>В CMake-скрипте приложения тоже надо сделать аналогичные правки</p>
<p><b>wxModularHost/CMakeLists.txt</b></p>
<pre class="brush: bash; title: ; notranslate">
if(APPLE)
	FOREACH(DEP_LIB ${DEMO_LIBS_SHARED})
		get_filename_component(ABS_ROOT_DIR ${PROJECT_ROOT_DIR} ABSOLUTE)
		set(LIBNAME_FULL &quot;${ABS_ROOT_DIR}/${DEP_LIB}/${OS_BASE_NAME}${LIB_SUFFIX}/$(CONFIGURATION)/lib${DEP_LIB}.dylib&quot;)
                add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD
                	COMMAND install_name_tool -change &quot;${LIBNAME_FULL}&quot; &quot;@executable_path/../Frameworks/lib${DEP_LIB}.dylib&quot; $&lt;TARGET_FILE:${EXECUTABLE_NAME}&gt;)
        ENDFOREACH(DEP_LIB)
endif(APPLE)
</pre>
<h3 id="v-zavershenie">В завершение</h3>
<p>Мы рассмотрели способ создания кросс-платформенных модульных приложений и сборку под Windows и Linux.<br />
Полный исходный код проекта, рассмотренного в статье, можно найти на GitHub: <a href="https://github.com/T-Rex/wxModularApp" title="Кросс-платформенное приложение с плагинами на wxWidgets">https://github.com/T-Rex/wxModularApp</a><br />
Также в GitHub-проекте есть дополнения к CMake-скриптам, которые позволяют собрать все под OS X.</p>
<p>PS: За время написания статьи вышла новая версия wxWidgets (3.0), с которой CMake еще не умеет работать (по крайней мере скрипты, которые работали для 2.9.x не отрабатывают с 3.0. Для тестирования лучше пока использовать код из ветки 2.9: svn.wxwidgets.org/svn/wx/wxWidgets/tags/WX_2_9_5/</p><p>The post <a href="https://wxwidgets.info/razrabotka-krossplatformennyx-modulnyx-prilozhenij-na-c-s-bibliotekoj-wxwidgets/">Разработка кроссплатформенных модульных приложений на C++ с библиотекой wxWidgets</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/razrabotka-krossplatformennyx-modulnyx-prilozhenij-na-c-s-bibliotekoj-wxwidgets/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>wxWidgets App With Plugins (Windows/Linux/Mac) &#8211; Sample Source Code</title>
		<link>https://wxwidgets.info/wxwidgets-app-with-plugins-windowslinuxmac-sample-source-code/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=wxwidgets-app-with-plugins-windowslinuxmac-sample-source-code</link>
					<comments>https://wxwidgets.info/wxwidgets-app-with-plugins-windowslinuxmac-sample-source-code/#respond</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Wed, 11 Sep 2013 16:35:43 +0000</pubDate>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[Plugins]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=607</guid>

					<description><![CDATA[<p>I can see that there is still a lot of topics at wxWidgets forums related to usage of shared libs or plugins with wxWidgets apps on different platform. For Windows it&#8217;s not hard to implement such app but on Linux and OS X this may be quite tricky (especially when you are not planning to&#8230;</p>
<p>The post <a href="https://wxwidgets.info/wxwidgets-app-with-plugins-windowslinuxmac-sample-source-code/">wxWidgets App With Plugins (Windows/Linux/Mac) – Sample Source Code</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>I can see that there is still a lot of topics at wxWidgets forums related to usage of shared libs or plugins with wxWidgets apps on different platform.</p>
<p>For Windows it&#8217;s not hard to implement such app but on Linux and OS X this may be quite tricky (especially when you are not planning to use installer for your app and create the app which will work on different machines without need to recompile it).</p>
<p>So, specially for those, who still has problems with implementation of plugins for wxWidgets apps, I created the sample which compiles and runs on all 3 platforms, has 2 types of plugins (with and without GUI), creates the relocatable application where all plugins and other libs search for dependencies using relative paths which means that you don&#8217;t need to rebuild the app on different machines to make it work.</p>
<p>You can find the complete source code at GitHub: <a href="/goto/https://github.com/T-Rex/wxModularApp">Modular wxWidgets Application with Plugins</a></p>
<p>The project is under development since I&#8217;m writing an article about this in parallel, so you are welcome to follow the project at GitHub and try the new versions.</p>
<p>&#8212;<br />
Features which I plan to add in nearest updates:</p>
<ul>
<li>Embedding of wxWidgets libs into OS X Bundle</li>
<li>Embedding of wxWidgets libs into application&#8217;s distro for Linux</li>
<li>Document/View: Support of different file formats using plugins</li>
<li>Communication between plugins</li>
</ul>
<p>So, stay tuned, start watching the project at GitHub!</p><p>The post <a href="https://wxwidgets.info/wxwidgets-app-with-plugins-windowslinuxmac-sample-source-code/">wxWidgets App With Plugins (Windows/Linux/Mac) – Sample Source Code</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/wxwidgets-app-with-plugins-windowslinuxmac-sample-source-code/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>wxToolBox is Now Open-Source!</title>
		<link>https://wxwidgets.info/wxtoolbox-is-now-open-source/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=wxtoolbox-is-now-open-source</link>
					<comments>https://wxwidgets.info/wxtoolbox-is-now-open-source/#respond</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Fri, 06 Sep 2013 22:47:00 +0000</pubDate>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[wxToolBox]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=600</guid>

					<description><![CDATA[<p>I&#8217;ve just published the source code of wxToolBox component and a couple of sample apps at GitHub: https://github.com/T-Rex/wxToolBox There is a working minimal sample and `Sample IDE` app with source code, Skin Editor does not compile with new version of wxPropertyGrid and I&#8217;m not currently interested in updating the source code. The working binary version for Windows&#8230;</p>
<p>The post <a href="https://wxwidgets.info/wxtoolbox-is-now-open-source/">wxToolBox is Now Open-Source!</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>I&#8217;ve just published the source code of wxToolBox component and a couple of sample apps at GitHub: <a href="/goto/https://github.com/T-Rex/wxToolBox">https://github.com/T-Rex/wxToolBox</a><br />
There is a working minimal sample and `Sample IDE` app with source code, Skin Editor does not compile with new version of wxPropertyGrid and I&#8217;m not currently interested in updating the source code. The working binary version for Windows can be downloaded from this page (also attached to this post): <a href="/goto/http://wxtoolbox.wxwidgets.info/docs/index.html">http://wxtoolbox.wxwidgets.info/docs/index.html</a></p>
<p>The source code is free for commercial and non-commercial use (component and demo apps).<br />
However if anybody still wants to buy me a beer or smth, you can make a kind of `donation` through ShareIt: <a href="/goto/http://tinyurl.com/kywtdj6">http://tinyurl.com/kywtdj6</a></p>
<p><a href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2013/09/SkinEditor.jpg"><img decoding="async" class="alignnone  wp-image-602" alt="SkinEditor" src="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2013/09/SkinEditor.jpg" width="596" height="547" srcset="https://wxwidgets.info/wordpress/wp-content/uploads/2013/09/SkinEditor.jpg 745w, https://wxwidgets.info/wordpress/wp-content/uploads/2013/09/SkinEditor-600x551.jpg 600w, https://wxwidgets.info/wordpress/wp-content/uploads/2013/09/SkinEditor-300x275.jpg 300w" sizes="(max-width: 600px) 100vw, 596px" /></a></p>
<p><a href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2013/09/wxtoolbox-skins.png"><img decoding="async" class="alignnone size-medium wp-image-603" alt="wxtoolbox-skins" src="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2013/09/wxtoolbox-skins-300x163.png" width="300" height="163" /></a></p>
<p><a href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2013/09/wxToolBoxSkinEditor-bin.zip">wxToolBoxSkinEditor Binary for Windows</a></p><p>The post <a href="https://wxwidgets.info/wxtoolbox-is-now-open-source/">wxToolBox is Now Open-Source!</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/wxtoolbox-is-now-open-source/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Microsoft Kinect Helper Library and Sample for wxWidgets</title>
		<link>https://wxwidgets.info/microsoft-kinect-helper-library-and-sample-for-wxwidgets/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=microsoft-kinect-helper-library-and-sample-for-wxwidgets</link>
					<comments>https://wxwidgets.info/microsoft-kinect-helper-library-and-sample-for-wxwidgets/#respond</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Fri, 01 Jul 2011 10:18:26 +0000</pubDate>
				<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[Kinect]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=593</guid>

					<description><![CDATA[<p>Microsoft released their Kinect SDK several days ago. So, for those wxWidgets developers who are interested in development of Kinect-powered applications, I created a small wxWidgets-based helper library and sample application which will allow to start using Kinect SDK. wxKinectHelper project is hosted at Google Code. For now only basic functionality is available: Retreiving the&#8230;</p>
<p>The post <a href="https://wxwidgets.info/microsoft-kinect-helper-library-and-sample-for-wxwidgets/">Microsoft Kinect Helper Library and Sample for wxWidgets</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Microsoft released their <a title="Microsoft Research Kinect SDK" href="//research.microsoft.com/en-us/um/redmond/projects/kinectsdk/" target="_blank" rel="noopener">Kinect SDK</a> several days ago.<br />
So, for those wxWidgets developers who are interested in development of Kinect-powered applications, I created a small wxWidgets-based helper library and sample application which will allow to start using Kinect SDK.</p>
<p><a title="wxKinectHelper - wxWidgets Wrapper for Kinect SDK" href="//code.google.com/p/wxkinecthelper/" target="_blank" rel="noopener">wxKinectHelper</a> project is hosted at Google Code.</p>
<p>For now only basic functionality is available:</p>
<ul>
<li>Retreiving the list of installed Kinect devices</li>
<li>Retrieving the IDs of devices</li>
<li>Starting/Stopping of frame grabbing</li>
<li>Grabbing depth images</li>
</ul>
<p>If you are willing to help with implementation of new features then just let me know. I will appreciate any help in improvement of current functionality.</p>
<p><strong>PS:</strong> To test the application and library you need to have Kinect device for sure.</p><p>The post <a href="https://wxwidgets.info/microsoft-kinect-helper-library-and-sample-for-wxwidgets/">Microsoft Kinect Helper Library and Sample for wxWidgets</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/microsoft-kinect-helper-library-and-sample-for-wxwidgets/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>wxJSON 1.1.0 Released</title>
		<link>https://wxwidgets.info/wxjson-1-1-0-released/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=wxjson-1-1-0-released</link>
					<comments>https://wxwidgets.info/wxjson-1-1-0-released/#comments</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Sat, 14 Nov 2009 14:07:08 +0000</pubDate>
				<category><![CDATA[Libraries]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[wxJSON]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=580</guid>

					<description><![CDATA[<p>JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition &#8211; December 1999. JSON is a text format that is completely language independent&#8230;</p>
<p>The post <a href="https://wxwidgets.info/wxjson-1-1-0-released/">wxJSON 1.1.0 Released</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition &#8211; December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.</p>
<p>The wxJSON library is a complete implementation of the JSON data-interchange format. All JSON specifications are implemented in this library plus some extensions in the writer and in the parser class.</p>
<p>Today <strong>wxJSON 1.1.0</strong> was announced. This release is compatible with both wxWidgets 2.8 and 2.9. It is also compatible with wxWidgets SVN HEAD. N<span>ow JSON reader and writer only process UTF-8 encoded text as a stream.<br />
Also added a new wxJSONValue&#8217;s member function to get values and fixed the bugs in wxJSONValue::IsSameAs()</span></p>
<ul>
<li><a href="//wxcode.sourceforge.net/docs/wxjson/" target="_blank" rel="noopener">Official home page of wxJSON</a></li>
<li><span><a title="wxJSON 1.1 Release Notes" href="//luccat.users.sourceforge.net/wxjson/docs/wxjson_whatsnew.html" target="_blank" rel="noopener">Read complete list of changes in this version</a><br />
</span></li>
</ul><p>The post <a href="https://wxwidgets.info/wxjson-1-1-0-released/">wxJSON 1.1.0 Released</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/wxjson-1-1-0-released/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>wxRuby. Оно даже работает!</title>
		<link>https://wxwidgets.info/wxruby-ono-dazhe-rabotaet/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=wxruby-ono-dazhe-rabotaet</link>
					<comments>https://wxwidgets.info/wxruby-ono-dazhe-rabotaet/#comments</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Thu, 03 Sep 2009 10:57:56 +0000</pubDate>
				<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[wxRuby]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=566</guid>

					<description><![CDATA[<p>Вдохновленнный читаемой нынче книгой My Job Went to India: 52 Ways to Save Your Job решил покорять новые горизонты. Наткнулся несколько дней назад на вводную статью о wxRuby и сегодня решил попробовать. Оказывается это не так страшно. Что понравилось: Под Windows ставится просто. После инсталляции не требует никаких настроек. Кодишь, запускаешь &#8211; работает. Документация, как&#8230;</p>
<p>The post <a href="https://wxwidgets.info/wxruby-ono-dazhe-rabotaet/">wxRuby. Оно даже работает!</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Вдохновленнный читаемой нынче книгой <a href="/goto/http://www.amazon.com/gp/product/0976694018?ie=UTF8&amp;tag=wxwidginfoart-20&amp;linkCode=as2&amp;camp=1789&amp;creative=9325&amp;creativeASIN=0976694018">My Job Went to India: 52 Ways to Save Your Job</a><img loading="lazy" decoding="async" class=" asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk asbeyqewjweushpscdfk" style="border:none !important; margin:0px !important;" src="https://www.assoc-amazon.com/e/ir?t=wxwidginfoart-20&amp;l=as2&amp;o=1&amp;a=0976694018" border="0" alt="" width="1" height="1" /> решил покорять новые горизонты. Наткнулся несколько дней назад на <a title="Вводная статья о wxRuby" href="//ruby-ua.blogspot.com/2009/09/wxruby-welcome-to-wxruby.html" target="_blank" rel="noopener">вводную статью о wxRuby</a> и сегодня решил попробовать. Оказывается это не так страшно.<br />
<span id="more-566"></span><br />
Что понравилось:</p>
<ul>
<li>Под Windows ставится просто. После инсталляции не требует никаких настроек. Кодишь, запускаешь &#8211; работает.</li>
<li>Документация, как всегда у wxWidgets, позволяет решить все вопросы.</li>
<li>Синтаксис&#8230; на вид &#8211; вменяемо (учитывая что для меня это новый язык программирования, минимальное приложение получилось создать минут за 15).</li>
<li>Есть бесплатные средства разработки.</li>
</ul>
<p>Что не понравилось:</p>
<ul>
<li>Бесплатные средства разработки немного&#8230;. унылые. Скорее напоминают просто текстовый редактор с подсветкой синтаксиса и возможностью запуска скриптов.</li>
<li>Без интеллисенса тяжело <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li>Хочу документацию в CHM, а есть только в HTML что не очень удобно</li>
</ul>
<p><a href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2009/09/freeride.jpg"><img loading="lazy" decoding="async" src="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2009/09/freeride.jpg" alt="FreeRIDE wxRuby IDE" title="FreeRIDE wxRuby IDE" width="556" height="586" class="alignnone size-full wp-image-567" srcset="https://wxwidgets.info/wordpress/wp-content/uploads/2009/09/freeride.jpg 556w, https://wxwidgets.info/wordpress/wp-content/uploads/2009/09/freeride-285x300.jpg 285w" sizes="(max-width: 600px) 100vw, 556px" /></a><br />
И вот&#8230; минимальное приложение. Как оно получилось:</p>
<pre class="brush: jscript; title: ; notranslate">
# -*- encoding: utf-8 -*-

require 'wx'

class HelloFrame &amp;lt; Wx::Frame
  def initialize
    super(nil, :title =&amp;gt; 'Hello World!')

    panel = Wx::Panel.new(self)

    sizer = Wx::BoxSizer.new(Wx::VERTICAL)
    panel.set_sizer(sizer)

    button = Wx::Button.new(panel, :label =&amp;gt; 'Press me')
    text = Wx::TextCtrl.new(panel, :value =&amp;gt; 'Sample', :style =&amp;gt; Wx::TE_MULTILINE)

    sizer.add(button, 0, Wx::EXPAND | Wx::ALL, 5)
    sizer.add(text, 1, Wx::EXPAND | Wx::LEFT | Wx::RIGHT | Wx::BOTTOM, 5)

    evt_button(button.get_id, :on_button_click)
  end

  def on_button_click
    Wx::MessageDialog.new(self, &amp;quot;Hello world!&amp;quot;, &amp;quot;Test&amp;quot;, Wx::OK).show_modal
  end
end

class HelloApp &amp;lt; Wx::App
  def on_init
    frame = HelloFrame.new
    frame.centre
    frame.show
  end
end

app = HelloApp.new
app.main_loop()
</pre>
<p>Немного странно называются классы, без префикса &#8220;wx&#8221;, зато с неймспейсом. Немного странным кажется способ привязки обработчиков событий. Очень понравилась возможность указывать значения только необходимых параметров. По сравнению с wxJavaScript мне wxRuby кажется более приятным т.к. все работает без дополнительной настройки. Сравнения по скорости работы пока не проводил. Есть поддержка wxAUI, wxGCDC/GdiPlus, Document/View, GLCanvas, что очень приятно.</p>
<p>Вместе с wxRuby поставляется довольно большое количество примеров, которые могут помочь разобраться как в синтаксисе так и в особенностях использования API.</p>
<ul>
<li><a href="http://rubyforge.org/frs/?group_id=35" target="_blank" rel="noopener">Скачать wxRuby</a></li>
<li><a title="Скачать FreeRIDE" href="http://rubyforge.org/project/showfiles.php?group_id=31" target="_blank" rel="noopener">Скачать FreeRIDE</a></li>
</ul><p>The post <a href="https://wxwidgets.info/wxruby-ono-dazhe-rabotaet/">wxRuby. Оно даже работает!</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/wxruby-ono-dazhe-rabotaet/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Дааа! Ribbon Bar для wxWidgets не за горами!</title>
		<link>https://wxwidgets.info/daaa-ribbon-bar-dlya-wxwidgets-ne-za-gorami/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=daaa-ribbon-bar-dlya-wxwidgets-ne-za-gorami</link>
					<comments>https://wxwidgets.info/daaa-ribbon-bar-dlya-wxwidgets-ne-za-gorami/#comments</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Wed, 02 Sep 2009 19:24:17 +0000</pubDate>
				<category><![CDATA[News]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=563</guid>

					<description><![CDATA[<p>Надо же,в wxBlog такое рассказывают. Оказывается Google Summer of Code для wxWidgets принес много полезного в этом году. Обещают поддержку нотификаций об изменениях файловой системы &#8211; wxFSWatcher, а также (внимание!) Ribbon для wxWidgets &#8211; wxRibbonBar. А еще&#8230;. апгрейды всевозможные для wxAUI (жду не дождусь collapsible panes и tabbed docking). Все это будет в svn trunk&#8230;</p>
<p>The post <a href="https://wxwidgets.info/daaa-ribbon-bar-dlya-wxwidgets-ne-za-gorami/">Дааа! Ribbon Bar для wxWidgets не за горами!</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Надо же,в wxBlog такое <a href="//wxwidgets.blogspot.com/2009/09/august-news.html" target="_blank" rel="noopener">рассказывают</a>. Оказывается Google Summer of Code для wxWidgets принес много полезного в этом году. Обещают поддержку нотификаций об изменениях файловой системы &#8211; wxFSWatcher, а также (внимание!) Ribbon для wxWidgets &#8211; wxRibbonBar. А еще&#8230;. апгрейды всевозможные для wxAUI (жду не дождусь collapsible panes и tabbed docking).</p>
<p>Все это будет в svn trunk и не факт что появится в ветке 2.8. Так что &#8220;кто куды, а мы к зайцам&#8221; (с), пойду качать svn trunk. Там кстати еще много чего полезного появилось. Если вы пользуетесь стабильной версией 2.8, рекомендую хотя бы попробовать trunk.</p>
<p>Еще вскользь упомянули, что потихоньку развивается wxSymbian и wxQNX. Порт для Symbian мне кажется очень перспективным, да.</p><p>The post <a href="https://wxwidgets.info/daaa-ribbon-bar-dlya-wxwidgets-ne-za-gorami/">Дааа! Ribbon Bar для wxWidgets не за горами!</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/daaa-ribbon-bar-dlya-wxwidgets-ne-za-gorami/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>Cross-Platform Way of Obtaining MAC Address of Your Machine</title>
		<link>https://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=cross-platform-way-of-obtaining-mac-address-of-your-machine</link>
					<comments>https://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/#comments</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Sun, 30 Aug 2009 12:04:45 +0000</pubDate>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<category><![CDATA[Networking]]></category>
		<category><![CDATA[wxWinCE]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=557</guid>

					<description><![CDATA[<p>In one of my current projects I had to implement client-server communication and protection by MAC address when client machine can&#8217;t connect to server if its MAC address is not allowed, regardless of network or broadband connection. But what was a surprise that wxWidgets does not have API which allows obtaining MAC address in cross-platform&#8230;</p>
<p>The post <a href="https://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/">Cross-Platform Way of Obtaining MAC Address of Your Machine</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>In one of my current projects I had to implement client-server communication and protection by MAC address when client machine can&#8217;t connect to server if its MAC address is not allowed, regardless of network or broadband connection. But what was a surprise that wxWidgets does not have API which allows obtaining MAC address in cross-platform way. So, I decided to write a small class which allows obtainig MAC address for Windows, Linux, Mac OS and Windows Mobile. Here it is:<br />
<span id="more-557"></span><br />
<strong>MACAddressUtility.h</strong></p>
<pre class="brush: cpp; title: ; notranslate">
#ifndef _MACADDRESS_UTILITY_H
#define _MACADDRESS_UTILITY_H

class MACAddressUtility
{
public:
	static long GetMACAddress(unsigned char * result);
private:
#if defined(WIN32) || defined(UNDER_CE)
	static long GetMACAddressMSW(unsigned char * result);
#elif defined(__APPLE__)
	static long GetMACAddressMAC(unsigned char * result);
#elif defined(LINUX) || defined(linux)
	static long GetMACAddressLinux(unsigned char * result);
#endif
};

#endif
</pre>
<p><strong>MACAddressUtility.cpp</strong></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;MACAddressUtility.h&quot;

#include &lt;stdio.h&gt;

#if defined(WIN32) || defined(UNDER_CE)
#	include &lt;windows.h&gt;
#	if defined(UNDER_CE)
#		include &lt;Iphlpapi.h&gt;
#	endif
#elif defined(__APPLE__)
#	include &lt;CoreFoundation/CoreFoundation.h&gt;
#	include &lt;IOKit/IOKitLib.h&gt;
#	include &lt;IOKit/network/IOEthernetInterface.h&gt;
#	include &lt;IOKit/network/IONetworkInterface.h&gt;
#	include &lt;IOKit/network/IOEthernetController.h&gt;
#elif defined(LINUX) || defined(linux)
#	include &lt;string.h&gt;
#	include &lt;net/if.h&gt;
#	include &lt;sys/ioctl.h&gt;
#   include &lt;sys/socket.h&gt;
#   include &lt;arpa/inet.h&gt;
#endif

long MACAddressUtility::GetMACAddress(unsigned char * result)
{
	// Fill result with zeroes
	memset(result, 0, 6);
	// Call appropriate function for each platform
#if defined(WIN32) || defined(UNDER_CE)
	return GetMACAddressMSW(result);
#elif defined(__APPLE__)
	return GetMACAddressMAC(result);
#elif defined(LINUX) || defined(linux)
	return GetMACAddressLinux(result);
#endif
	// If platform is not supported then return error code
	return -1;
}

#if defined(WIN32) || defined(UNDER_CE)

inline long MACAddressUtility::GetMACAddressMSW(unsigned char * result)
{

#if defined(UNDER_CE)
	IP_ADAPTER_INFO AdapterInfo&#x5B;16]; // Allocate information
	DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
	if(GetAdaptersInfo(AdapterInfo, &amp;dwBufLen) == ERROR_SUCCESS)
	{
		memcpy(result, AdapterInfo-&gt;Address, 6);
	}
	else return -1;
#else
	UUID uuid;
	if(UuidCreateSequential(&amp;uuid) == RPC_S_UUID_NO_ADDRESS) return -1;
	memcpy(result, (char*)(uuid.Data4+2), 6);
#endif
	return 0;
}

#elif defined(__APPLE__)

static kern_return_t FindEthernetInterfaces(io_iterator_t *matchingServices)
{
    kern_return_t		kernResult;
    CFMutableDictionaryRef	matchingDict;
    CFMutableDictionaryRef	propertyMatchDict;

    matchingDict = IOServiceMatching(kIOEthernetInterfaceClass);

    if (NULL != matchingDict)
	{
        propertyMatchDict =
			CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
				&amp;kCFTypeDictionaryKeyCallBacks,
				&amp;kCFTypeDictionaryValueCallBacks);

        if (NULL != propertyMatchDict)
		{
            CFDictionarySetValue(propertyMatchDict,
				CFSTR(kIOPrimaryInterface), kCFBooleanTrue);
            CFDictionarySetValue(matchingDict,
				CFSTR(kIOPropertyMatchKey), propertyMatchDict);
            CFRelease(propertyMatchDict);
        }
    }
    kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault,
		matchingDict, matchingServices);
    return kernResult;
}

static kern_return_t GetMACAddress(io_iterator_t intfIterator,
								   UInt8 *MACAddress, UInt8 bufferSize)
{
    io_object_t		intfService;
    io_object_t		controllerService;
    kern_return_t	kernResult = KERN_FAILURE;

	if (bufferSize &lt; kIOEthernetAddressSize) {
		return kernResult;
	}

    bzero(MACAddress, bufferSize);

    while (intfService = IOIteratorNext(intfIterator))
    {
        CFTypeRef	MACAddressAsCFData;

        // IONetworkControllers can't be found directly by the IOServiceGetMatchingServices call,
        // since they are hardware nubs and do not participate in driver matching. In other words,
        // registerService() is never called on them. So we've found the IONetworkInterface and will
        // get its parent controller by asking for it specifically.

        // IORegistryEntryGetParentEntry retains the returned object,
		// so release it when we're done with it.
        kernResult =
			IORegistryEntryGetParentEntry(intfService,
				kIOServicePlane,
				&amp;controllerService);

        if (KERN_SUCCESS != kernResult) {
            printf(&quot;IORegistryEntryGetParentEntry returned 0x%08x\n&quot;, kernResult);
        }
        else {
            // Retrieve the MAC address property from the I/O Registry in the form of a CFData
            MACAddressAsCFData =
				IORegistryEntryCreateCFProperty(controllerService,
					CFSTR(kIOMACAddress),
					kCFAllocatorDefault,
					0);
            if (MACAddressAsCFData) {
                CFShow(MACAddressAsCFData); // for display purposes only; output goes to stderr

                // Get the raw bytes of the MAC address from the CFData
                CFDataGetBytes((CFDataRef)MACAddressAsCFData,
					CFRangeMake(0, kIOEthernetAddressSize), MACAddress);
                CFRelease(MACAddressAsCFData);
            }

            // Done with the parent Ethernet controller object so we release it.
            (void) IOObjectRelease(controllerService);
        }

        // Done with the Ethernet interface object so we release it.
        (void) IOObjectRelease(intfService);
    }

    return kernResult;
}

long MACAddressUtility::GetMACAddressMAC(unsigned char * result)
{
	io_iterator_t	intfIterator;
	kern_return_t	kernResult = KERN_FAILURE;
	do
	{
		kernResult = ::FindEthernetInterfaces(&amp;intfIterator);
		if (KERN_SUCCESS != kernResult) break;
	    kernResult = ::GetMACAddress(intfIterator, (UInt8*)result, 6);
    }
	while(false);
    (void) IOObjectRelease(intfIterator);
}

#elif defined(LINUX) || defined(linux)

long MACAddressUtility::GetMACAddressLinux(unsigned char * result)
{
	struct ifreq ifr;
    struct ifreq *IFR;
    struct ifconf ifc;
    char buf&#x5B;1024];
    int s, i;
    int ok = 0;

    s = socket(AF_INET, SOCK_DGRAM, 0);
    if (s == -1)
	{
        return -1;
    }

    ifc.ifc_len = sizeof(buf);
    ifc.ifc_buf = buf;
    ioctl(s, SIOCGIFCONF, &amp;ifc);

    IFR = ifc.ifc_req;
    for (i = ifc.ifc_len / sizeof(struct ifreq); --i &gt;= 0; IFR++)
	{
        strcpy(ifr.ifr_name, IFR-&gt;ifr_name);
        if (ioctl(s, SIOCGIFFLAGS, &amp;ifr) == 0)
		{
            if (! (ifr.ifr_flags &amp; IFF_LOOPBACK))
			{
                if (ioctl(s, SIOCGIFHWADDR, &amp;ifr) == 0)
				{
                    ok = 1;
                    break;
                }
            }
        }
    }

    shutdown(s, SHUT_RDWR);
    if (ok)
	{
        bcopy( ifr.ifr_hwaddr.sa_data, result, 6);
    }
    else
	{
        return -1;
    }
    return 0;
}

#endif
</pre>
<p><strong>wxMACAddressUtility.h</strong></p>
<pre class="brush: cpp; title: ; notranslate">
#ifndef _WX_MACADDRESS_UTILITY_H
#define _WX_MACADDRESS_UTILITY_H

#include &quot;MACAddressUtility.h&quot;
#include &lt;wx/wx.h&gt;

class wxMACAddressUtility
{
public:
	static wxString GetMACAddress()
	{
		unsigned char result&#x5B;6];
		if(MACAddressUtility::GetMACAddress(result) == 0)
		{
			return wxString::Format(wxT(&quot;%02X:%02X:%02X:%02X:%02X:%02X&quot;),
				(unsigned int)result&#x5B;0], (unsigned int)result&#x5B;1], (unsigned int)result&#x5B;2],
				(unsigned int)result&#x5B;3], (unsigned int)result&#x5B;4], (unsigned int)result&#x5B;5]);
		}
		return wxEmptyString;
	}
};

#endif
</pre>
<p><strong>Sample of usage:</strong></p>
<pre class="brush: cpp; title: ; notranslate">
m_MACAddress = wxMACAddressUtility::GetMACAddress();
</pre>
<p><a href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2009/08/macaddr.jpg"><img loading="lazy" decoding="async" src="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2009/08/macaddr.jpg" alt="Get MAC address programmatically" title="Get MAC address programmatically" width="226" height="67" class="alignnone size-full wp-image-558" /></a></p>
<p>As you can see, core class does not depend on wxWidgets and can be used in native applications written without wxWidgets.</p>
<p><a href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2009/08/wxMACAddressUtility.7z" title="How to get MAC address in C++: Windows, Linux, Mac OS, Windows Mobile">Download sample application</a></p><p>The post <a href="https://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/">Cross-Platform Way of Obtaining MAC Address of Your Machine</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/cross-platform-way-of-obtaining-mac-address-of-your-machine/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Taking Screenshots with wxWidgets under Mac OS is Really Tricky.</title>
		<link>https://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky</link>
					<comments>https://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/#comments</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Thu, 21 May 2009 21:30:00 +0000</pubDate>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[Articles]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=550</guid>

					<description><![CDATA[<p>Taking screenshots is a very common task and it was a must for one of my current projects. What was a surprise when I understood that my favourite toolkit can&#8217;t do that in cross-platform manner. It is official bug that wxScreenDC does not work properly under Mac OS and you can&#8217;t use Blit() message for&#8230;</p>
<p>The post <a href="https://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/">Taking Screenshots with wxWidgets under Mac OS is Really Tricky.</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Taking screenshots is a very common task and it was a must for one of my current projects. What was a surprise when I understood that my favourite toolkit can&#8217;t do that in cross-platform manner.</p>
<p>It is <a href="http://trac.wxwidgets.org/ticket/9486">official bug</a> that wxScreenDC does not work properly under Mac OS and you can&#8217;t use Blit() message for copying screen onto wxMemoryDC.</p>
<p>After digging the Internet I found a kind of solution which used OpenGL and created wxWidgets-based class which takes screenshots also under Mac OS. It was really hard task for me because I haven&#8217;t used neither Carbon nor Cocoa before. However everything works now and I&#8217;m happy.</p>
<p>Here it is:<br />
<span id="more-550"></span><br />
<strong>wxScreenshotMaker.h</strong></p>
<pre class="brush: cpp; title: ; notranslate">
#pragma once

#include &lt;wx/wx.h&gt;
#ifdef __WXMAC__
#include &lt;OpenGL/OpenGL.h&gt;
#endif

class wxScreenshotMaker
{
public:
	wxScreenshotMaker();
	~wxScreenshotMaker();
	wxBitmap GetScreenshot();
private:
#ifdef __WXMAC__

	int screenWidth;
	int screenHeight;

	CGLContextObj glContextObj;
	int rowSize;
	unsigned char * glBitmapData;
	unsigned char * glRowData;

	void InitOpenGL();
	void GrabGLScreen();
	void SwizzleBitmap();
	void FinalizeOpenGL();
#endif
};
</pre>
<p><strong>wxScreenshotMaker.cpp</strong></p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;wxScreenshotMaker.h&quot;
#ifndef __WXMAC__
#include &lt;wx/dcscreen.h&gt;
#else
#include &lt;CoreFoundation/CoreFoundation.h&gt;
#include &lt;ApplicationServices/ApplicationServices.h&gt;
#include &lt;OpenGL/gl.h&gt;
#endif

wxScreenshotMaker::wxScreenshotMaker()
#ifdef __WXMAC__
: screenWidth(0), screenHeight(0), glBitmapData(NULL), glRowData(NULL)
#endif
{
#ifdef __WXMAC__
	wxSize size = wxGetDisplaySize();
	screenWidth = size.GetWidth();
	screenHeight = size.GetHeight();
	InitOpenGL();
#endif
}

wxScreenshotMaker::~wxScreenshotMaker()
{
#ifdef __WXMAC__
	FinalizeOpenGL();
#endif
}

wxBitmap wxScreenshotMaker::GetScreenshot()
{
#ifndef __WXMAC__
	wxScreenDC screenDC;
	wxBitmap bmp(screenDC.GetSize().GetWidth(), screenDC.GetSize().GetHeight());
	wxMemoryDC mdc(bmp);
	mdc.Blit(0, 0, bmp.GetWidth(), bmp.GetHeight(), &amp;screenDC, 0, 0);
	mdc.SelectObject(wxNullBitmap);
	return bmp;
#else
	if(glBitmapData)
	{
		GrabGLScreen();
		wxImage img = wxImage(screenWidth, screenHeight, glBitmapData, true);
		return wxBitmap(img.Copy());
	}
#endif
}

#ifdef __WXMAC__

void wxScreenshotMaker::InitOpenGL()
{
	do
	{
		rowSize = screenWidth * 3;
		rowSize = (rowSize + 2) &amp; ~2;

		glRowData = (unsigned char *)realloc(glRowData, rowSize);
		glBitmapData = (unsigned char *)realloc(glBitmapData, rowSize * screenHeight);

		bzero(glRowData, rowSize);
		bzero(glBitmapData, rowSize * screenHeight);

		CGDirectDisplayID display = CGMainDisplayID();
		CGLPixelFormatObj pixelFormatObj ;
		GLint numPixelFormats;
		CGLPixelFormatAttribute attribs&#x5B;] =
		{
			kCGLPFAFullScreen,
			kCGLPFADisplayMask,
			(CGLPixelFormatAttribute)0,    /* Display mask bit goes here */
			(CGLPixelFormatAttribute)0
		};

		attribs&#x5B;2] = (CGLPixelFormatAttribute) CGDisplayIDToOpenGLDisplayMask(display);

		/* Build a full-screen GL context */
		CGLChoosePixelFormat( attribs, &amp;pixelFormatObj, &amp;numPixelFormats );
		if ( pixelFormatObj == NULL ) break;
		CGLCreateContext( pixelFormatObj, NULL, &amp;glContextObj ) ;
		CGLDestroyPixelFormat( pixelFormatObj ) ;
		if ( glContextObj == NULL ) break;
		CGLSetCurrentContext( glContextObj ) ;
		CGLSetFullScreen( glContextObj ) ;
	}
	while(false);
}

void wxScreenshotMaker::FinalizeOpenGL()
{
    CGLSetCurrentContext( NULL );
    CGLClearDrawable( glContextObj );
    CGLDestroyContext( glContextObj );
	free(glRowData);
	free(glBitmapData);
}

void wxScreenshotMaker::GrabGLScreen()
{
	glReadBuffer(GL_FRONT);
    /* Read framebuffer into our bitmap */
    glFinish();
    glPixelStorei(GL_PACK_ALIGNMENT, 3);
    glPixelStorei(GL_PACK_ROW_LENGTH, 0);
    glPixelStorei(GL_PACK_SKIP_ROWS, 0);
    glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
    /*
      * Fetch the data in RGB format, matching the bitmap context.
      */
    glReadPixels((GLint)0, (GLint)0, (GLint)screenWidth, (GLint)screenHeight,
		GL_RGB, GL_BYTE, glBitmapData);

	SwizzleBitmap();
}

void wxScreenshotMaker::SwizzleBitmap()
{
	int top, bottom;
	top = 0;
    bottom = screenHeight - 1;
	void * base = glBitmapData;
	void * topP = NULL;
	void * bottomP = NULL;

	while(top &lt; bottom)
	{
		topP = (void *)((top * rowSize) + (intptr_t)base);
        bottomP = (void *)((bottom * rowSize) + (intptr_t)base);

		bcopy( topP, glRowData, rowSize );
        bcopy( bottomP, topP, rowSize );
        bcopy( glRowData, bottomP, rowSize );

        ++top;
        --bottom;
	}
}

#endif
</pre>
<p><strong>Sample</strong></p>
<pre class="brush: cpp; title: ; notranslate">
wxScreenshotMaker screenshot;
m_Canvas-&gt;SetBitmap(screenshot.GetScreenshot());
m_Canvas-&gt;Refresh();
</pre>
<p><a title="Download source code: Take screenshot with wxWidgets under Mac OS" href="https://staging.wxwidgets.info/wordpress/wp-content/uploads/2009/05/wxscreenshotmaker.zip">Download wxScreenshotMaker and sample project</a>.</p><p>The post <a href="https://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/">Taking Screenshots with wxWidgets under Mac OS is Really Tricky.</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/taking-screenshots-with-wxwidgets-under-mac-os-is-really-tricky/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>AxTk: An Accessibility Toolkit for wxWidgets</title>
		<link>https://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=axtk-an-accessibility-toolkit-for-wxwidgets</link>
					<comments>https://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/#comments</comments>
		
		<dc:creator><![CDATA[T-Rex]]></dc:creator>
		<pubDate>Thu, 07 May 2009 16:07:54 +0000</pubDate>
				<category><![CDATA[Components]]></category>
		<category><![CDATA[Libraries]]></category>
		<category><![CDATA[News]]></category>
		<category><![CDATA[wxWidgets]]></category>
		<guid isPermaLink="false">https://wxwidgets.info/?p=546</guid>

					<description><![CDATA[<p>http://code.google.com/p/axtk/ What is AxTk? AxTk (pronounced Ay Ex Tee Kay) is an open source, C++ add-on for wxWidgets that helps developers create highly accessible, talking applications for users with impaired vision. It may also be useful for other impairments that benefit from a simplified user interface. AxTk features a new menu-based system that is easy&#8230;</p>
<p>The post <a href="https://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/">AxTk: An Accessibility Toolkit for wxWidgets</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></description>
										<content:encoded><![CDATA[<p><a href="http://code.google.com/p/axtk/" target="_blank" rel="noopener">http://code.google.com/p/axtk/</a></p>
<h4 id="what-is-axtk">What is AxTk?</h4>
<p><strong>AxTk</strong> (pronounced Ay Ex Tee Kay) <strong>is an open source, C++ add-on for wxWidgets that helps developers create highly accessible, talking applications for users with impaired vision</strong>. It may also be useful for other impairments that benefit from a simplified user interface.</p>
<p>AxTk features a new menu-based system that is easy to learn and use, in addition to providing adaptation for some existing GUI controls and dialogs. The developer can choose whether to use the menu system, or to adapt an existing application UI, or use a combination of methods.</p>
<p>AxTk is cross-platform (tested so far on Windows XP, Linux and Mac OS X 10.5), and includes text-to-speech classes with the ability to drive SAPI 5, Apple Speech Synthesis Manager, eSpeak, and Cepstral. Other speech engines can be driven by writing additional handlers.</p>
<p>Note that AxTk is a work in progress and the API is subject to change.<br />
<span id="more-546"></span></p>
<h4 id="what-else-is-axtk">What else is AxTk?</h4>
<p>There is an additional, higher-level layer for building applications that help the user maintain &#8216;resource libraries&#8217; for all kinds of files and services. The motivation is to provided simplified access to many resources and activities that are currently spread amongst many applications and web sites. By making this part of AxTk, we increase the chances of providing a really useful, accessible application platform that can be customised in interesting ways for individuals or government sectors. However, this is currently a less well-developed aspect of AxTk and one which you can ignore fo now. The resource demo shows playing of audio albums and reading Epub books (having converted the XHTML content to plain text first).</p>
<h4 id="what-about-braille-output">What about Braille output?</h4>
<p>Although nothing is specifically coded yet for Braille devices, they could be supported relatively easily by adding handlers to the wxTextToSpeech system, to output to the device instead of a speech engine.</p>
<h4 id="why-not-just-use-a-screen-reader">Why not just use a screen reader?</h4>
<p>Screen readers don&#8217;t have an in-depth knowledge of the application; in theory, an AxTk application can make use of knowledge of its own structure to make speech more helpful and less verbose. Also, an application&#8217;s custom controls are not accessible to screen readers. In additon, AxTk provides an alternative, menu-based user interface that is not so prone to the problems of accessing conventional layouts via a screen reader. Finally, an AxTk application provides support for application-wide colour and text size changes that are harder to achieve in an ad-hoc way.</p>
<h4 id="is-this-anything-to-do-with-msaa-microsoft-active-accessibility">Is this anything to do with MSAA (Microsoft Active Accessibility)?</h4>
<p>No; a different approach is used. Although wxWidgets has some basic MSAA support, other ports have no similar support and it may be hard to create a comprehensive cross-platform solution to accessibility based on OS-level accessibility support. It would still leave applications and users at the mercy of particular screen readers and it would not solve other problems that AxTk sets out to solve: visual adaptation, fine control over speech (such as content voicing), customisable shortcut support, and more.</p>
<p>Having said that, work on integrating MSAA equivalents for other platforms would be very welcome, either as part of AxTk or built into wxWidgets.</p>
<h4 id="how-hard-is-it-to-use">How hard is it to use?</h4>
<p>Apart from some housekeeping that you can pinch from the sample, the amount of extra code can be small if you&#8217;re just adapting existing UIs. For example, to make a dialog self-voicing and responsive to visual appearance changes (assuming the dialog&#8217;s controls are in the set of controls currently handled by AxTk):</p>
<pre class="brush: cpp; title: ; notranslate">
#include &quot;ax/ax_ui_adaptation.h&quot;
...
class MyDialog: public wxDialog
{
    ...
    AxSelfVoicing m_adapter;
};

MyDialog::MyDialog(...)
{
    ...
    m_adapter.Adapt(this);
}
</pre>
<p>If you have controls that are not currently handled by AxTk, you can write adapter classes and also handler classes that detect the appropriate controls and create their adapters.</p>
<p>Using the new menu system is also pretty straightforward as you can see from the sample (mainframe.cpp). There are some new concepts such as AxActivator, which handles menu item activation, but there&#8217;s plenty in common with conventional wxWidgets programming.</p>
<h4 id="where-and-how-do-i-get-it">Where and how do I get it?</h4>
<p>AxTk is hosted at Google Code and is available via SVN or as a source tarball or zip file.</p>
<p><a href="http://code.google.com/p/axtk/" target="_blank" rel="noopener">http://code.google.com/p/axtk/</a></p>
<p>The discussion group is axtk-dev_at_googlegroups.com.</p>
<p>The documentation is available here:</p>
<p><a href="http://www.anthemion.co.uk/axtk/html/index.html" target="_blank" rel="noopener">http://www.anthemion.co.uk/axtk/html/index.html</a></p>
<h4 id="are-there-demos">Are there demos?</h4>
<p>Yes; you can download axsample binaries for Windows, Linux and Mac from:</p>
<p><a href="http://www.anthemion.co.uk/axtk/" target="_blank" rel="noopener">http://www.anthemion.co.uk/axtk/</a></p>
<p>You can ignore axresourcesample unless you&#8217;re interested in the higher-level resource library layer mentioned previously.</p>
<h4 id="can-i-contribute">Can I contribute?</h4>
<p>Please do! See docs/todo.txt for some of the things left to do. You&#8217;ll probably find things to do yourself. You can supply patches in the AxTk&#8217;s Issue Tracker, or you can ask to be added to the list of project members in order to make changes in SVN.</p>
<h4 id="whats-the-build-system">What&#8217;s the build system?</h4>
<p>None to speak of. This would make a good contribution. Currently you can use DialogBlocks to generate projects and makefiles for the two samples, which simply include all the AxTk code.</p>
<h4 id="what-is-the-license">What is the license?</h4>
<p>The license is the New BSD License, which is very brief, easy to understand, and industry-friendly. Please see license.txt in the source tarball for details.</p>
<p>I hope you find AxTk useful &#8211; or at least, fun to play with.</p><p>The post <a href="https://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/">AxTk: An Accessibility Toolkit for wxWidgets</a> first appeared on <a href="https://wxwidgets.info">wxWidgets</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://wxwidgets.info/axtk-an-accessibility-toolkit-for-wxwidgets/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
	</channel>
</rss>
