Repository: cunnane/xloil
Branch: master
Commit: 21c0f866351d
Files: 680
Total size: 4.6 MB
Directory structure:
gitextract__p1idbhy/
├── .gitattributes
├── .gitignore
├── .readthedocs.yml
├── .runsettings
├── FUNDING.yml
├── LICENSE
├── README.md
├── Version.txt
├── config/
│ ├── xloil.ini
│ ├── xloil_local.ini
│ └── xloil_local_32.ini
├── docs/
│ ├── Excel Function Numbers.xlsx
│ ├── make.cmd
│ ├── requirements.txt
│ ├── source/
│ │ ├── Concepts.rst
│ │ ├── Core.rst
│ │ ├── Developer.rst
│ │ ├── Events.rst
│ │ ├── Introduction.rst
│ │ ├── conf.py
│ │ ├── index.rst
│ │ ├── xlOil_Core_Functions.rst
│ │ ├── xlOil_Cpp/
│ │ │ ├── COM.rst
│ │ │ ├── CppRtd.rst
│ │ │ ├── CustomGUI.rst
│ │ │ ├── DynamicRegistration.rst
│ │ │ ├── Events.rst
│ │ │ ├── GettingStarted.rst
│ │ │ ├── ObjectHandles.rst
│ │ │ ├── SpecialArgs.rst
│ │ │ ├── StaticXLLs.rst
│ │ │ └── index.rst
│ │ ├── xlOil_Python/
│ │ │ ├── BuiltInUDFs.rst
│ │ │ ├── Concepts.rst
│ │ │ ├── CustomGUI.rst
│ │ │ ├── Debugging.rst
│ │ │ ├── DistributingAddins.rst
│ │ │ ├── Dynamic.rst
│ │ │ ├── Example.rst
│ │ │ ├── ExampleGUI.rst
│ │ │ ├── ExampleRTD.rst
│ │ │ ├── ExcelApplication.rst
│ │ │ ├── ExternalPackages.rst
│ │ │ ├── FAQ.rst
│ │ │ ├── Functions.rst
│ │ │ ├── GettingStarted.rst
│ │ │ ├── Jupyter.rst
│ │ │ ├── ModuleReference.rst
│ │ │ ├── Rtd.rst
│ │ │ ├── TypeConversion.rst
│ │ │ └── index.rst
│ │ ├── xlOil_SQL/
│ │ │ └── index.rst
│ │ ├── xlOil_Utils.rst
│ │ └── xloil.doxyfile
│ └── xlOil-Sphinx.code-workspace
├── external/
│ ├── Excel2013SDK/
│ │ ├── INCLUDE/
│ │ │ └── XLCALL.H
│ │ ├── LIB/
│ │ │ ├── WIn32/
│ │ │ │ └── XLCALL32.LIB
│ │ │ └── x64/
│ │ │ └── XLCALL32.LIB
│ │ └── SRC/
│ │ ├── ReadMe.txt
│ │ └── XLCALL.CPP
│ ├── Sources.txt
│ └── boost-1.67/
│ └── boost/
│ └── preprocessor/
│ ├── arithmetic/
│ │ ├── add.hpp
│ │ ├── dec.hpp
│ │ ├── detail/
│ │ │ └── div_base.hpp
│ │ ├── div.hpp
│ │ ├── inc.hpp
│ │ ├── mod.hpp
│ │ ├── mul.hpp
│ │ └── sub.hpp
│ ├── arithmetic.hpp
│ ├── array/
│ │ ├── data.hpp
│ │ ├── detail/
│ │ │ └── get_data.hpp
│ │ ├── elem.hpp
│ │ ├── enum.hpp
│ │ ├── insert.hpp
│ │ ├── pop_back.hpp
│ │ ├── pop_front.hpp
│ │ ├── push_back.hpp
│ │ ├── push_front.hpp
│ │ ├── remove.hpp
│ │ ├── replace.hpp
│ │ ├── reverse.hpp
│ │ ├── size.hpp
│ │ ├── to_list.hpp
│ │ ├── to_seq.hpp
│ │ └── to_tuple.hpp
│ ├── array.hpp
│ ├── assert_msg.hpp
│ ├── cat.hpp
│ ├── comma.hpp
│ ├── comma_if.hpp
│ ├── comparison/
│ │ ├── equal.hpp
│ │ ├── greater.hpp
│ │ ├── greater_equal.hpp
│ │ ├── less.hpp
│ │ ├── less_equal.hpp
│ │ └── not_equal.hpp
│ ├── comparison.hpp
│ ├── config/
│ │ ├── config.hpp
│ │ └── limits.hpp
│ ├── control/
│ │ ├── deduce_d.hpp
│ │ ├── detail/
│ │ │ ├── dmc/
│ │ │ │ └── while.hpp
│ │ │ ├── edg/
│ │ │ │ └── while.hpp
│ │ │ ├── msvc/
│ │ │ │ └── while.hpp
│ │ │ └── while.hpp
│ │ ├── expr_if.hpp
│ │ ├── expr_iif.hpp
│ │ ├── if.hpp
│ │ ├── iif.hpp
│ │ └── while.hpp
│ ├── control.hpp
│ ├── debug/
│ │ ├── assert.hpp
│ │ ├── error.hpp
│ │ └── line.hpp
│ ├── debug.hpp
│ ├── dec.hpp
│ ├── detail/
│ │ ├── auto_rec.hpp
│ │ ├── check.hpp
│ │ ├── dmc/
│ │ │ └── auto_rec.hpp
│ │ ├── is_binary.hpp
│ │ ├── is_nullary.hpp
│ │ ├── is_unary.hpp
│ │ ├── null.hpp
│ │ └── split.hpp
│ ├── empty.hpp
│ ├── enum.hpp
│ ├── enum_params.hpp
│ ├── enum_params_with_a_default.hpp
│ ├── enum_params_with_defaults.hpp
│ ├── enum_shifted.hpp
│ ├── enum_shifted_params.hpp
│ ├── expand.hpp
│ ├── expr_if.hpp
│ ├── facilities/
│ │ ├── apply.hpp
│ │ ├── detail/
│ │ │ └── is_empty.hpp
│ │ ├── empty.hpp
│ │ ├── expand.hpp
│ │ ├── identity.hpp
│ │ ├── intercept.hpp
│ │ ├── is_1.hpp
│ │ ├── is_empty.hpp
│ │ ├── is_empty_or_1.hpp
│ │ ├── is_empty_variadic.hpp
│ │ └── overload.hpp
│ ├── facilities.hpp
│ ├── for.hpp
│ ├── identity.hpp
│ ├── if.hpp
│ ├── inc.hpp
│ ├── iterate.hpp
│ ├── iteration/
│ │ ├── detail/
│ │ │ ├── bounds/
│ │ │ │ ├── lower1.hpp
│ │ │ │ ├── lower2.hpp
│ │ │ │ ├── lower3.hpp
│ │ │ │ ├── lower4.hpp
│ │ │ │ ├── lower5.hpp
│ │ │ │ ├── upper1.hpp
│ │ │ │ ├── upper2.hpp
│ │ │ │ ├── upper3.hpp
│ │ │ │ ├── upper4.hpp
│ │ │ │ └── upper5.hpp
│ │ │ ├── finish.hpp
│ │ │ ├── iter/
│ │ │ │ ├── forward1.hpp
│ │ │ │ ├── forward2.hpp
│ │ │ │ ├── forward3.hpp
│ │ │ │ ├── forward4.hpp
│ │ │ │ ├── forward5.hpp
│ │ │ │ ├── reverse1.hpp
│ │ │ │ ├── reverse2.hpp
│ │ │ │ ├── reverse3.hpp
│ │ │ │ ├── reverse4.hpp
│ │ │ │ └── reverse5.hpp
│ │ │ ├── local.hpp
│ │ │ ├── rlocal.hpp
│ │ │ ├── self.hpp
│ │ │ └── start.hpp
│ │ ├── iterate.hpp
│ │ ├── local.hpp
│ │ └── self.hpp
│ ├── iteration.hpp
│ ├── library.hpp
│ ├── limits.hpp
│ ├── list/
│ │ ├── adt.hpp
│ │ ├── append.hpp
│ │ ├── at.hpp
│ │ ├── cat.hpp
│ │ ├── detail/
│ │ │ ├── dmc/
│ │ │ │ └── fold_left.hpp
│ │ │ ├── edg/
│ │ │ │ ├── fold_left.hpp
│ │ │ │ └── fold_right.hpp
│ │ │ ├── fold_left.hpp
│ │ │ └── fold_right.hpp
│ │ ├── enum.hpp
│ │ ├── filter.hpp
│ │ ├── first_n.hpp
│ │ ├── fold_left.hpp
│ │ ├── fold_right.hpp
│ │ ├── for_each.hpp
│ │ ├── for_each_i.hpp
│ │ ├── for_each_product.hpp
│ │ ├── rest_n.hpp
│ │ ├── reverse.hpp
│ │ ├── size.hpp
│ │ ├── to_array.hpp
│ │ ├── to_seq.hpp
│ │ ├── to_tuple.hpp
│ │ └── transform.hpp
│ ├── list.hpp
│ ├── logical/
│ │ ├── and.hpp
│ │ ├── bitand.hpp
│ │ ├── bitnor.hpp
│ │ ├── bitor.hpp
│ │ ├── bitxor.hpp
│ │ ├── bool.hpp
│ │ ├── compl.hpp
│ │ ├── nor.hpp
│ │ ├── not.hpp
│ │ ├── or.hpp
│ │ └── xor.hpp
│ ├── logical.hpp
│ ├── max.hpp
│ ├── min.hpp
│ ├── punctuation/
│ │ ├── comma.hpp
│ │ ├── comma_if.hpp
│ │ ├── detail/
│ │ │ └── is_begin_parens.hpp
│ │ ├── is_begin_parens.hpp
│ │ ├── paren.hpp
│ │ ├── paren_if.hpp
│ │ └── remove_parens.hpp
│ ├── punctuation.hpp
│ ├── repeat.hpp
│ ├── repeat_2nd.hpp
│ ├── repeat_3rd.hpp
│ ├── repeat_from_to.hpp
│ ├── repeat_from_to_2nd.hpp
│ ├── repeat_from_to_3rd.hpp
│ ├── repetition/
│ │ ├── deduce_r.hpp
│ │ ├── deduce_z.hpp
│ │ ├── detail/
│ │ │ ├── dmc/
│ │ │ │ └── for.hpp
│ │ │ ├── edg/
│ │ │ │ └── for.hpp
│ │ │ ├── for.hpp
│ │ │ └── msvc/
│ │ │ └── for.hpp
│ │ ├── enum.hpp
│ │ ├── enum_binary_params.hpp
│ │ ├── enum_params.hpp
│ │ ├── enum_params_with_a_default.hpp
│ │ ├── enum_params_with_defaults.hpp
│ │ ├── enum_shifted.hpp
│ │ ├── enum_shifted_binary_params.hpp
│ │ ├── enum_shifted_params.hpp
│ │ ├── enum_trailing.hpp
│ │ ├── enum_trailing_binary_params.hpp
│ │ ├── enum_trailing_params.hpp
│ │ ├── for.hpp
│ │ ├── repeat.hpp
│ │ └── repeat_from_to.hpp
│ ├── repetition.hpp
│ ├── selection/
│ │ ├── max.hpp
│ │ └── min.hpp
│ ├── selection.hpp
│ ├── seq/
│ │ ├── cat.hpp
│ │ ├── detail/
│ │ │ ├── binary_transform.hpp
│ │ │ ├── is_empty.hpp
│ │ │ ├── split.hpp
│ │ │ └── to_list_msvc.hpp
│ │ ├── elem.hpp
│ │ ├── enum.hpp
│ │ ├── filter.hpp
│ │ ├── first_n.hpp
│ │ ├── fold_left.hpp
│ │ ├── fold_right.hpp
│ │ ├── for_each.hpp
│ │ ├── for_each_i.hpp
│ │ ├── for_each_product.hpp
│ │ ├── insert.hpp
│ │ ├── pop_back.hpp
│ │ ├── pop_front.hpp
│ │ ├── push_back.hpp
│ │ ├── push_front.hpp
│ │ ├── remove.hpp
│ │ ├── replace.hpp
│ │ ├── rest_n.hpp
│ │ ├── reverse.hpp
│ │ ├── seq.hpp
│ │ ├── size.hpp
│ │ ├── subseq.hpp
│ │ ├── to_array.hpp
│ │ ├── to_list.hpp
│ │ ├── to_tuple.hpp
│ │ ├── transform.hpp
│ │ └── variadic_seq_to_seq.hpp
│ ├── seq.hpp
│ ├── slot/
│ │ ├── counter.hpp
│ │ ├── detail/
│ │ │ ├── counter.hpp
│ │ │ ├── def.hpp
│ │ │ ├── shared.hpp
│ │ │ ├── slot1.hpp
│ │ │ ├── slot2.hpp
│ │ │ ├── slot3.hpp
│ │ │ ├── slot4.hpp
│ │ │ └── slot5.hpp
│ │ └── slot.hpp
│ ├── slot.hpp
│ ├── stringize.hpp
│ ├── tuple/
│ │ ├── detail/
│ │ │ └── is_single_return.hpp
│ │ ├── eat.hpp
│ │ ├── elem.hpp
│ │ ├── enum.hpp
│ │ ├── insert.hpp
│ │ ├── pop_back.hpp
│ │ ├── pop_front.hpp
│ │ ├── push_back.hpp
│ │ ├── push_front.hpp
│ │ ├── rem.hpp
│ │ ├── remove.hpp
│ │ ├── replace.hpp
│ │ ├── reverse.hpp
│ │ ├── size.hpp
│ │ ├── to_array.hpp
│ │ ├── to_list.hpp
│ │ └── to_seq.hpp
│ ├── tuple.hpp
│ ├── variadic/
│ │ ├── detail/
│ │ │ └── is_single_return.hpp
│ │ ├── elem.hpp
│ │ ├── size.hpp
│ │ ├── to_array.hpp
│ │ ├── to_list.hpp
│ │ ├── to_seq.hpp
│ │ └── to_tuple.hpp
│ ├── variadic.hpp
│ ├── while.hpp
│ └── wstringize.hpp
├── include/
│ └── xloil/
│ ├── AppObjects.h
│ ├── ArrayBuilder.h
│ ├── Async.h
│ ├── AutoBind.h
│ ├── Caller.h
│ ├── Date.h
│ ├── DynamicRegister.h
│ ├── EnumHelper.h
│ ├── Events.h
│ ├── ExcelArray.h
│ ├── ExcelCall.h
│ ├── ExcelObj.h
│ ├── ExcelObjCache.h
│ ├── ExcelRef.h
│ ├── ExcelThread.h
│ ├── ExcelTypeLib.h
│ ├── ExcelUI.h
│ ├── ExportMacro.h
│ ├── FPArray.h
│ ├── FuncSpec.h
│ ├── Interface.h
│ ├── Limits.h
│ ├── Log.h
│ ├── LogWindow.h
│ ├── NumericTypeConverters.h
│ ├── ObjectCache.h
│ ├── PString.h
│ ├── Plugin.h
│ ├── Preprocessor.h
│ ├── Range.h
│ ├── Register.h
│ ├── RtdServer.h
│ ├── State.h
│ ├── StaticRegister.h
│ ├── StringUtils.h
│ ├── Throw.h
│ ├── TypeConverters.h
│ ├── Version.h
│ ├── WindowsSlim.h
│ ├── XlCallSlim.h
│ ├── XllEntryPoint.h
│ └── xlOil.h
├── libs/
│ ├── SetRoot.props
│ ├── xlOilPlugin.props
│ ├── xlOilStaticLib.props
│ ├── xlOil_Python/
│ │ ├── ArrayHelpers.h
│ │ ├── AsyncFunctions.cpp
│ │ ├── AsyncFunctions.h
│ │ ├── CPython.h
│ │ ├── EventLoop.cpp
│ │ ├── EventLoop.h
│ │ ├── Loader/
│ │ │ ├── StubLoader.cpp
│ │ │ └── xlOil_Python.vcxproj
│ │ ├── Main.cpp
│ │ ├── Package/
│ │ │ ├── TestConfig.py
│ │ │ ├── generate_stubs.py
│ │ │ ├── setup.py
│ │ │ ├── test_JupyterConnection.py
│ │ │ ├── test_PythonAutomation.py
│ │ │ ├── test_SpreadsheetRunner.py
│ │ │ ├── xlOil_Python_Package.pyproj
│ │ │ └── xloil/
│ │ │ ├── __init__.py
│ │ │ ├── _core.py
│ │ │ ├── _event_loop.py
│ │ │ ├── _paths.py
│ │ │ ├── _pyinstaller/
│ │ │ │ ├── __init__.py
│ │ │ │ └── hook-xloil.py
│ │ │ ├── _superreload.py
│ │ │ ├── com.py
│ │ │ ├── command_line.py
│ │ │ ├── debug.py
│ │ │ ├── excelfuncs.py
│ │ │ ├── func_inspect.py
│ │ │ ├── gui/
│ │ │ │ ├── __init__.py
│ │ │ │ ├── qt_console.py
│ │ │ │ ├── qtpy.py
│ │ │ │ ├── tk_console.py
│ │ │ │ ├── tkinter.py
│ │ │ │ └── wx.py
│ │ │ ├── importer.py
│ │ │ ├── jupyter.py
│ │ │ ├── jupyter_kernel.py
│ │ │ ├── logging.py
│ │ │ ├── matplotlib.py
│ │ │ ├── pandas.py
│ │ │ ├── pillow.py
│ │ │ ├── register.py
│ │ │ ├── rtd.py
│ │ │ ├── stubs/
│ │ │ │ ├── __init__.py
│ │ │ │ └── xloil_core/
│ │ │ │ ├── __init__.py
│ │ │ │ └── event/
│ │ │ │ └── __init__.py
│ │ │ ├── type_converters.py
│ │ │ └── xloil_ribbon.py
│ │ ├── PyAddin.cpp
│ │ ├── PyAddin.h
│ │ ├── PyAddress.cpp
│ │ ├── PyAddress.h
│ │ ├── PyAppCallRun.cpp
│ │ ├── PyAppCallRun.h
│ │ ├── PyAppObjects.cpp
│ │ ├── PyCOM.cpp
│ │ ├── PyCOM.h
│ │ ├── PyCache.cpp
│ │ ├── PyCache.h
│ │ ├── PyCore.cpp
│ │ ├── PyCore.h
│ │ ├── PyEvents.cpp
│ │ ├── PyEvents.h
│ │ ├── PyFunctionRegister.cpp
│ │ ├── PyFunctionRegister.h
│ │ ├── PyFuture.h
│ │ ├── PyHelpers.cpp
│ │ ├── PyHelpers.h
│ │ ├── PyImage.cpp
│ │ ├── PyImage.h
│ │ ├── PyLogWriter.cpp
│ │ ├── PyObjectGC.cpp
│ │ ├── PyRibbon.cpp
│ │ ├── PyRtd.cpp
│ │ ├── PyRtd.h
│ │ ├── PyRunLater.cpp
│ │ ├── PySettings.props
│ │ ├── PySettings310.props
│ │ ├── PySettings311.props
│ │ ├── PySettings312.props
│ │ ├── PySettings313.props
│ │ ├── PySettings314.props
│ │ ├── PySettings36.props
│ │ ├── PySettings37.props
│ │ ├── PySettings38.props
│ │ ├── PySettings39.props
│ │ ├── PySource.cpp
│ │ ├── PySource.h
│ │ ├── PyStatusBar.cpp
│ │ ├── TypeConversion/
│ │ │ ├── BasicTypes.cpp
│ │ │ ├── BasicTypes.h
│ │ │ ├── ConverterInterface.h
│ │ │ ├── Numpy.h
│ │ │ ├── NumpyDatetime.cpp
│ │ │ ├── NumpyDatetime.h
│ │ │ ├── NumpyFromExcel.cpp
│ │ │ ├── NumpyHelpers.cpp
│ │ │ ├── NumpyHelpers.h
│ │ │ ├── NumpyPandas.cpp
│ │ │ ├── NumpyToExcel.cpp
│ │ │ ├── PyCustomType.cpp
│ │ │ ├── PyDateType.cpp
│ │ │ ├── PyDateType.h
│ │ │ ├── PyDictType.cpp
│ │ │ ├── PyDictType.h
│ │ │ ├── PyExcelArrayType.cpp
│ │ │ ├── PyExcelArrayType.h
│ │ │ ├── PyRangeType.cpp
│ │ │ ├── PyTupleType.cpp
│ │ │ └── PyTupleType.h
│ │ ├── xlOil_Python310.vcxproj
│ │ ├── xlOil_Python311.vcxproj
│ │ ├── xlOil_Python312.vcxproj
│ │ ├── xlOil_Python313.vcxproj
│ │ ├── xlOil_Python314.vcxproj
│ │ ├── xlOil_Python36.vcxproj
│ │ ├── xlOil_Python37.vcxproj
│ │ ├── xlOil_Python38.vcxproj
│ │ ├── xlOil_Python39.vcxproj
│ │ └── xlOil_Python39.vcxproj.filters
│ ├── xlOil_SQL/
│ │ ├── Cache.cpp
│ │ ├── Cache.h
│ │ ├── Common.cpp
│ │ ├── Common.h
│ │ ├── Main.cpp
│ │ ├── XlArrayTable.cpp
│ │ ├── XlArrayTable.h
│ │ ├── xlOil_SQL.vcxproj
│ │ ├── xlOil_SQL.vcxproj.filters
│ │ ├── xloSql.cpp
│ │ ├── xloSqlDB.cpp
│ │ ├── xloSqlQuery.cpp
│ │ ├── xloSqlTable.cpp
│ │ └── xloSqlTables.cpp
│ └── xlOil_Utils/
│ ├── Main.cpp
│ ├── RegexHelpers.h
│ ├── xlOil_Utils.vcxproj
│ ├── xloBlock.cpp
│ ├── xloConcat.cpp
│ ├── xloFill.cpp
│ ├── xloFillNA.cpp
│ ├── xloIndex.cpp
│ ├── xloPad.cpp
│ ├── xloRegex.cpp
│ ├── xloSearch.cpp
│ ├── xloSort.cpp
│ └── xloSplit.cpp
├── src/
│ ├── BuildPaths.props
│ ├── Common.props
│ ├── Debug.props
│ ├── Release.props
│ ├── external/
│ │ ├── asmjit.vcxproj
│ │ ├── rdcfswatcher.vcxproj
│ │ └── spdlog.vcxproj
│ ├── xlOil/
│ │ ├── Interface.cpp
│ │ ├── Loaders/
│ │ │ ├── AddinLoader.cpp
│ │ │ ├── AddinLoader.h
│ │ │ ├── CoreEntryPoint.cpp
│ │ │ ├── CoreEntryPoint.h
│ │ │ ├── PluginLoader.cpp
│ │ │ └── PluginLoader.h
│ │ ├── Log.cpp
│ │ ├── xlOil.vcxproj
│ │ └── xlOil.vcxproj.filters
│ ├── xlOil-COM/
│ │ ├── API/
│ │ │ ├── ComUtils.cpp
│ │ │ ├── Events.cpp
│ │ │ ├── ExcelRange.cpp
│ │ │ ├── ExcelThread.cpp
│ │ │ └── RtdServer.cpp
│ │ ├── AppObjects.cpp
│ │ ├── ClassFactory.cpp
│ │ ├── ClassFactory.h
│ │ ├── ComAddin.cpp
│ │ ├── ComAddin.h
│ │ ├── ComEventSink.cpp
│ │ ├── ComEventSink.h
│ │ ├── ComVariant.cpp
│ │ ├── ComVariant.h
│ │ ├── Connect.cpp
│ │ ├── Connect.h
│ │ ├── CustomTaskPane.cpp
│ │ ├── CustomTaskPane.h
│ │ ├── RibbonExtensibility.cpp
│ │ ├── RibbonExtensibility.h
│ │ ├── RtdAsyncManager.cpp
│ │ ├── RtdAsyncManager.h
│ │ ├── RtdManager.cpp
│ │ ├── RtdManager.h
│ │ ├── RtdServerWorker.h
│ │ ├── TaskPaneHostControl.cpp
│ │ ├── TaskPaneHostControl.h
│ │ ├── WorkbookScopeFunctions.cpp
│ │ ├── WorkbookScopeFunctions.h
│ │ ├── XllContextInvoke.cpp
│ │ ├── XllContextInvoke.h
│ │ ├── xlOil-COM.vcxproj
│ │ └── xlOil-COM.vcxproj.filters
│ ├── xlOil-Dynamic/
│ │ ├── DynamicRegistration.cpp
│ │ ├── ExternalRegionAllocator.h
│ │ ├── LocalFunctions.cpp
│ │ ├── LocalFunctions.h
│ │ ├── PEHelper.cpp
│ │ ├── PEHelper.h
│ │ ├── Thunker.cpp
│ │ ├── Thunker.h
│ │ ├── xlOil-Dynamic.vcxproj
│ │ └── xlOil-Dynamic.vcxproj.filters
│ ├── xlOil-Funcs/
│ │ ├── ExcelObjCache.cpp
│ │ ├── xlOil-Funcs.vcxproj
│ │ ├── xlOil-Funcs.vcxproj.filters
│ │ ├── xloHelp.cpp
│ │ ├── xloLog.cpp
│ │ ├── xloReload.cpp
│ │ └── xloVersion.cpp
│ ├── xlOil-Loader/
│ │ ├── LoaderEntryPoint.cpp
│ │ ├── xlOil-Loader.vcxproj
│ │ └── xlOilAddin.def
│ ├── xlOil-XLL/
│ │ ├── ArrayBuilder.cpp
│ │ ├── Async.cpp
│ │ ├── Caller.cpp
│ │ ├── Date.cpp
│ │ ├── ExcelArray.cpp
│ │ ├── ExcelCall.cpp
│ │ ├── ExcelCallMapping.h
│ │ ├── ExcelObj.cpp
│ │ ├── ExcelRef.cpp
│ │ ├── FPArray.cpp
│ │ ├── FuncRegistry.cpp
│ │ ├── FuncRegistry.h
│ │ ├── Intellisense.cpp
│ │ ├── Intellisense.h
│ │ ├── Log.cpp
│ │ ├── LogWindow.cpp
│ │ ├── LogWindowSink.cpp
│ │ ├── LogWindowSink.h
│ │ ├── State.cpp
│ │ ├── StaticRegister.cpp
│ │ ├── Throw.cpp
│ │ ├── XlCall.cpp
│ │ ├── XllEvents.cpp
│ │ ├── xlOil-XLL.vcxproj
│ │ └── xlOil-XLL.vcxproj.filters
│ └── xlOilHelpers/
│ ├── Environment.cpp
│ ├── Environment.h
│ ├── Exception.h
│ ├── GuidUtils.cpp
│ ├── GuidUtils.h
│ ├── Settings.cpp
│ ├── Settings.h
│ ├── Utils.h
│ └── xlOilHelpers.vcxproj
├── tests/
│ ├── AutoSheets/
│ │ ├── PythonTest.py
│ │ ├── PythonTest.xlsx
│ │ ├── TestModule.py
│ │ ├── TestSQL.xlsx
│ │ ├── TestUtils.ipynb
│ │ └── TestUtils.xlsx
│ ├── CodePageConversion.cpp
│ ├── Date.cpp
│ ├── Environment.cpp
│ ├── ManualSheets/
│ │ ├── python/
│ │ │ ├── PySpeedTest.py
│ │ │ ├── PySpeedTest.xlsm
│ │ │ ├── PythonRTDTest.py
│ │ │ ├── PythonRTDTest.xlsx
│ │ │ ├── PythonTestAsync.py
│ │ │ ├── PythonTestAsync.xlsm
│ │ │ ├── PythonTestUI.py
│ │ │ ├── PythonTestUI.xlsm
│ │ │ ├── TestJupyter.xlsx
│ │ │ └── TestJupyterConnection.ipynb
│ │ └── rtd/
│ │ └── RtdTest.xlsx
│ ├── PString.cpp
│ ├── TestAddin/
│ │ ├── AutoBind.cpp
│ │ ├── CacheTest.cpp
│ │ ├── CacheTest2.cpp
│ │ ├── CallbackTest.cpp
│ │ ├── ExoticArgTest.cpp
│ │ ├── Main.cpp
│ │ ├── RtdServerTest.cpp
│ │ ├── TestAddin.vcxproj
│ │ ├── TestAddin.vcxproj.filters
│ │ └── TestTestAddin.xlsx
│ ├── TestArrayBuilder.cpp
│ ├── TestCOM.cpp
│ ├── TestCache.cpp
│ ├── TestExcelCall.cpp
│ ├── TestExcelObj.cpp
│ ├── TestGuid.cpp
│ ├── TestRange.cpp
│ ├── TestSimpleAllocator.cpp
│ ├── TestStringUtils.cpp
│ ├── TestTempFile.cpp
│ ├── TestThunker.cpp
│ ├── Tests.vcxproj
│ └── Tests.vcxproj.filters
├── tools/
│ ├── BuildRelease.cmd
│ ├── DownloadDependencies.cmd
│ ├── WriteInclude.ps1
│ ├── WriteVersion.ps1
│ ├── stage.py
│ ├── xlOil_Install.ps1
│ ├── xlOil_NewAddin.ps1
│ ├── xlOil_RegistryClean.ps1
│ └── xlOil_Remove.ps1
└── xlOil.sln
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto
================================================
FILE: .gitignore
================================================
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
# Executables
*.out
*.app
# Visual studio stuff
.vs
build/
packages/
graveyard/
*.vcxproj.user
.vscode
# Other caches
__pycache__
.ipynb_checkpoints
# External libraries
/external/python/
/external/sqlite
/external/pybind11-stubgen
/external/CTPL
/external/tomlplusplus
/external/spdlog
/external/pybind11
/external/rdcfswatcher
/external/asmjit
/external/winreg
/docs/source/_build
~$*
================================================
FILE: .readthedocs.yml
================================================
# .readthedocs.yml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py
build:
os: "ubuntu-22.04"
tools:
python: "3.11"
python:
install:
- requirements: docs/requirements.txt
================================================
FILE: .runsettings
================================================
build/x64/Debug
================================================
FILE: FUNDING.yml
================================================
github: [cunnane]
================================================
FILE: LICENSE
================================================
Apache License
Copyright 2020 Steven Cunnane
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
xlOil
=====
xlOil provides framework for interacting with Excel in different programming
languages. It gives a way to write functions in a language and have them
appear in Excel as worksheet functions or macros or have them control GUI elements.
For example, the Python bindings can replace VBA in almost all use cases
and even provide functionality not available from VBA.
xlOil is designed to have very low overheads when calling your worksheet
functions.
xlOil supports different languages via plugins. The languages currently
supported are:
- C++
- Python
- SQL
In addition there is *xlOil_Utils* which contains some handy tools which Microsoft
never quite got around to adding.
The latest stable documentation is here: https://xloil.readthedocs.io/en/stable.
xlOil features
--------------
* Python
- Concise syntax to declare an Excel function
- Optional type checking of function parameters
- Supports keyword arguments
- Choice of globally declared functions or code modules limited to a single workbook
like VBA workbook-level functions
- Tight integration with *numpy* - very low overheads for array functions
- Understands python tuples, lists, dictionarys and *pandas* dataframes
- Async functions
- RTD functions and on-the-fly RTD server creation
- Macro type functions which write to ranges on the sheet
- Access to the Excel Application object and the full Excel object model
- Drive Excel through COM automation
- Hook Excel events
- Pass any python object back to Excel and back into another function
- Simple add-in deployment
- Two-way connection to *Jupyter* notebooks: run worksheet functions in *Jupyter* and query variables
in the *jupyter* kernel
- Create Ribbon toolbars and Custom Task Panes
- Return *matplotlib* plots and images from worksheet functions
* C++
- Safe and convenient wrappers around most things in the C-API
- Concise syntax to declare Excel functions: registration is automatic
- Deal with Excel variants, Ranges, Arrays and strings in a natural C++ fashion
- Object cache allows returning opaque objects to Excel and passing them back to other functions
- Simplified RTD server creation
- RTD-based background calculation
- Create Ribbon toolbars and Custom Task Panes
* SQL
- Create tables from Excel ranges and arrays
- Query and join them with the full sqlite3 SQL syntax
* Utils: very fast functions to:
- Sort on multiple columns
- Split and join strings
- Make arrays from blocks
Supporting other languages
--------------------------
You can use xlOil as an end-user of these plugins or you can use it to write
you own language bindings (and ideally add them to the repo).
================================================
FILE: Version.txt
================================================
0.22.1
================================================
FILE: config/xloil.ini
================================================
#
######## xlOil Settings #########
#
# This is a TOML file.
#
#################################
#
[Addin]
##### Plugins to load
#
# Load these plugins. The directory containing xloil.dll is searched
# first, then the normal DLL search path order.
#
Plugins=["xlOil_Python", "xlOil_SQL", "xlOil_Utils"]
#
# Load any plugins in the same directory as the core dll which
# match this pattern.
#
#PluginSearchPattern="xloil_*.dll"
##### Log file settings
#
# Intensity of logging, choose from:
# "trace", "debug", "info", "warning", "error", "critical", "off"
#
LogLevel="warning"
#
# Level at which xlOil will pop up a log window to display recent
# log entries
#
LogPopupLevel="error"
#
# Level at which xlOil will flush the log file to disk, lower levels
# decrease performance but can be useful for debugging crashes
#
#LogFlushLevel="warning"
#
# The log file is created at .log and in the same
# directory unless specified below
#
#LogFile="my.log"
#
# Log file rotation is controlled by these two parameters. A single
# log file can grow to the *LogMaxSize* in Kb. Once this limit is
# reached, a new blank log file is started, existing old log files
# are renamed and the oldest is deleted to ensure maximum of
# *LogNumberOfFiles* files.
#
#LogMaxSize=512
#LogNumberOfFiles=2
# If you have an ini file at %APPDATA%\xlOil\xlOil.ini, the core xlOil.dll
# is loaded using those settings before any other xlOil-based XLL. Since only one
# instance of xlOil can be hosted in Excel, one settings file must take precedence.
# The assumption is that if you have xlOil installed, you want those settings to
# be the primary ones. The below option, when used in a XLL-specific ini file,
# allows it to be loaded before the core.
# LoadBeforeCore=False
##### Date
#
# The date formats xlOil will attempt to parse for a string to date
# conversion. Syntax follows C++ get_time here:
# https://en.cppreference.com/w/cpp/io/manip/get_time
# Note the date parsing is case sensitive because get_time is case
# sensitive on Windows.
#
DateFormats=["%Y-%m-%d", "%Y%b%d"]
#
# The key XLOIL_PATH is edited by the xlOil_Install powershell script
# Note: Use [[]] syntax because the order of Environment variables matters
#
[[Addin.Environment]]
XLOIL_PATH=""
[[Addin.Environment]]
PATH='''%PATH%;%XLOIL_PATH%'''
##### Python Plugin Settings
#
[xlOil_Python]
#
# Python modules to load on start up. Must be on python's sys.path.
# xlOil provides the following optional modules:
# * xloil.xloil_ribbon: adds a ribbon toolbar with some useful functions
# * xloil.jupyter: adds jupyter interaction
#
LoadModules=["xloil.xloil_ribbon"]
#
# On workbook open, look for a python file matching this template
# where * is replaced by the Excel workbook name
#
WorkbookModule="*.py"
#
# Look for a module matching this pattern in the directory of the
# XLL when it is loaded where '*' is replaced with the addin name.
# The default is "*.py" which means an ini file is optional for a
# simple addin
#
#AddinModule="*.py"
#
# Calls Py_SetPath before initialising python. This allows explict
# specification of python's sys.path rather than using the usual
# search routine, which overrides any setting of PYTHONPATH.
# Generally this option is not required.
#
#SetSysPath='''.'''
#
# Sets the library for COM support. This is used by the `xloil.app`
# and `to_com` functions although can be override on a per-call basis.
# The default is 'win32com', 'comtypes' is also available.
#
#ComLib="comtypes"
#
# Selects the debugger to use. The choices are:
# * Visual Studio 2019+ : no need to pre-select this, just attach to
# a running Excel process
# * VS Code: choose `vscode` or `debugpy`. The `DebugPyPort` attribute
# then determines the port on which the server listens.
# * Pdb: choose 'pdb'
#
Debugger = ""
#
# Determines the port on which the server listens.
#
DebugPyPort = "5678"
#
# By default xlOil loads python modules in a background thread when the addin
# starts up or a local worksheet module is loaded. This avoids blocking the
# UI but can lead to #NAME! errors if the a workbook is calculated before
# the relevant functions are registered. Set UseLoaderThread to false to
# disable background loading
#
#UseLoaderThread=false
#
# When error propagation is enabled, if any function argument is an error code
# (#N/A!, #NUM!, etc.) that error code is given as the function's return value,
# otherwise all argument values are handled by the function. Error propagation
# is consistent with how Excel built-in functions handle errors, it improves
# performance in the presence of error codes and it allow's Excel's error tracing
# to function.
#
# Currently this setting is respected only by the xlOil_Python plugin.
#
#ErrorPropagation=false
#
# List of path prefixes which will be ignored for autoreloading on file
# modification detection.
#
#AutoReloadExcludePaths = []
##### Python Environment
#
# We need to set the python environment paths. Usually this is done automatically
# by `xloil install` or `xloil create` or the ribbon.
#
# Usually just set PYTHONEXECUTABLE - python should be able to figure out the rest.
# xlOil also uses this variable to help detect the presence of a virtual Environment.
# Alternatively set the PYTHONPATH, PYTHONHOME and PATH directly as required by your
# distribution.
#
# Note environment vars can be set by dynamically expanding a registry key.
#
[[xlOil_Python.Environment]]
#
# Tells python to do case-insensitve module name lookup and so minimises
# surprises on windows when creating workbook and addin modules. Recommended!
#
PYTHONCASEOK="1"
#
# If this environment variable is set, xlOil tries to load the corresponding
# pythonXY.dll as the interpreter. If unset or blank, xlOil uses the first python
# version discovered using the paths set below.
#
XLOIL_PYTHON_VERSION=""
#
# Additional paths to add to python's sys.path. Prefer to add user search
# paths here and reserve the PYTHONPATH setting below for system paths.
# (We make the empty value a semi-colon so that the variable exists
# for the environment variable expansion below)
XLOIL_PYTHON_PATH=";"
[[xlOil_Python.Environment]]
#
# PYTHONEXECUTABLE should point to the python.exe of the current environment.
# Note it is possible to dynamically expand registry keys contained in angle
# brackets <>.
#
PYTHONEXECUTABLE=''''''
PYTHONPATH='''%PYTHONPATH%;%XLOIL_PYTHON_PATH%'''
================================================
FILE: config/xloil_local.ini
================================================
#
######## xlOil Dev Settings ###########################################
#
# This is a version of xlOil.ini which is copied to the build directory
# when xloil.xll is built. Its purpose is to configure dev settings
# without accidentally overwriting the distriuted xloil.ini file
#
#######################################################################
#
[Addin]
##### Plugins to load
#
# Load these plugins. The directory containing xloil.dll is searched
# first, then the normal DLL search path order.
#
Plugins=["xlOil_Python", "xlOil_SQL", "xlOil_Utils"]
##### Log file settings
#
# Intensity of logging, choose from:
# "trace", "debug", "info", "warning", "error", "critical", "off"
#
LogLevel="trace"
#
# Level at which xlOil will pop up a log window to display recent
# log entries
#
LogPopupLevel="error"
#
# Level at which xlOil will flush the log file to disk, lower levels
# decrease performance but can be useful for debugging crashes
#
#LogFlushLevel="warning"
#
# The log file is created at .log and in the same
# directory unless specified below
#
#LogFile="my.log"
#
# Log file rotation is controlled by these two parameters. A single
# log file can grow to the *LogMaxSize* in Kb. Once this limit is
# reached, a new blank log file is started, existing old log files
# are renamed and the oldest is deleted to ensure maximum of
# *LogNumberOfFiles* files.
#
#LogMaxSize=512
#LogNumberOfFiles=2
# If you have an ini file at %APPDATA%\xlOil\xlOil.ini, the core xlOil.dll
# is loaded using those settings before any other xlOil-based XLL. The assumption
# is that you have xlOil installed, but since only one instance of xlOil
# can be hosted in Excel, one settings file must take precedence. The below option
# allows an XLL to be loaded before the core.
# LoadBeforeCore=false
##### Date
#
# The date formats xlOil will attempt to parse for a string to date
# conversion. Syntax follows C++ get_time here:
# https://en.cppreference.com/w/cpp/io/manip/get_time
# Note the date parsing is case sensitive because get_time is case
# sensitive on Windows.
#
DateFormats=["%Y-%m-%d", "%Y%b%d"]
#
# The key XLOIL_PATH is edited by the xlOil_Install powershell script
# Note: Use [[]] syntax because the order of Environment variables matters
#
#[[Addin.Environment]]
#XLOIL_PATH=""
#[[Addin.Environment]]
#PATH='''%PATH%;%XLOIL_PATH%'''
##### Python Plugin Settings
#
[xlOil_Python]
#
# Python modules to load on start up. Must be on python's sys.path
#
LoadModules=["xloil.xloil_ribbon"]
#
# On workbook open, look for a python file matching this template
# where * is replaced by the Excel workbook name
#
WorkbookModule="*.py"
#
# Look for a module matching this pattern in the directory of the
# XLL when it is loaded where '*' is replaced with the addin name.
# The default is "*.py" which means an ini file is optional for a
# simple addin
#
#AddinModule="*.py"
#
# Calls Py_SetPath before initialising python. This allows explict
# specification of python's sys.path rather than using the usual
# search routine, which overrides any setting of PYTHONPATH.
# Generally this option is not required.
#
#SetSysPath='''.'''
#
# Sets the library for COM support. This is used by the `xloil.app`
# and `to_com` functions although can be override on a per-call basis.
# The default is 'win32com', 'comtypes' is also available.
#
#ComLib="comtypes"
#
# Selects the debugger to use. The choices are:
# * Visual Studio 2019+ : no need to pre-select this, just attach to
# a running Excel process
# * VS Code: choose `vscode` or `debugpy`. The `DebugPyPort` attribute
# then determines the port on which the server listens.
# * Pdb: choose 'pdb'
#
Debugger = ""
#
# Determines the port on which the server listens.
#
DebugPyPort = "5678"
#UseLoaderThread=false
ErrorPropagation=false
##### Python Environment
#
# We need to set the python environment paths. Usually this is done automatically
# by `xloil install` or `xloil create` or the ribbon.
#
# Usually just set PYTHONEXECUTABLE - python should be able to figure out the rest.
# xlOil also uses this variable to help detect the presence of a virtual Environment.
# Alternatively set the PYTHONPATH, PYTHONHOME and PATH directly as required by your
# distribution.
#
# Note environment vars can be set by dynamically expanding a registry key.
#
[xlOil_Python.Environment]
PYTHONCASEOK="1"
#
# If this environment variable is set, xlOil tries to load the corresponding
# pythonXY.dll as the interpreter. If unset or blank, xlOil uses the reported
# python version of the interpreter in the first python dist found.
#
XLOIL_PYTHON_VERSION=""
#
# PYTHONEXECUTABLE should point to the python.exe of the current environment.
# Note it is possible to dynamically expand registry keys contained in angle
# brackets <>.
#
PYTHONEXECUTABLE='''D:\lib\conda311\python.exe'''
#PYTHONEXECUTABLE=''''''
XLOIL_PYTHON_PATH='''D:\lib'''
PYTHONPATH='''%PYTHONPATH%;D:\lib'''
================================================
FILE: config/xloil_local_32.ini
================================================
#
######## xlOil Dev Settings ###########################################
#
# This is a version of xlOil.ini which is copied to the build directory
# when xloil.xll is built. Its purpose is to configure dev settings
# without accidentally overwriting the distriuted xloil.ini file
#
#######################################################################
#
[Addin]
##### Plugins to load
#
# Load these plugins. The directory containing xloil.dll is searched
# first, then the normal DLL search path order.
#
Plugins=["xlOil_Python", "xlOil_SQL", "xlOil_Utils"]
##### Log file settings
#
# Intensity of logging, choose from:
# "trace", "debug", "info", "warning", "error", "critical", "off"
#
LogLevel="trace"
#
# Level at which xlOil will pop up a log window to display recent
# log entries
#
LogPopupLevel="error"
#
# The log file is created at .log and in the same
# directory unless specified below
#
#LogFile="my.log"
#
# Log file rotation is controlled by these two parameters. A single
# log file can grow to the *LogMaxSize* in Kb. Once this limit is
# reached, a new blank log file is started, existing old log files
# are renamed and the oldest is deleted to ensure maximum of
# *LogNumberOfFiles* files.
#
#LogMaxSize=512
#LogNumberOfFiles=2
# If you have an ini file at %APPDATA%\xlOil\xlOil.ini, the core xlOil.dll
# is loaded using those settings before any other xlOil-based XLL. The assumption
# is that you have xlOil installed, but since only one instance of xlOil
# can be hosted in Excel, one settings file must take precedence. The below option
# allows an XLL to be loaded before the core.
# LoadBeforeCore=false
##### Date
#
# The date formats xlOil will attempt to parse for a string to date
# conversion. Syntax follows C++ get_time here:
# https://en.cppreference.com/w/cpp/io/manip/get_time
# Note the date parsing is case sensitive because get_time is case
# sensitive on Windows.
#
DateFormats=["%Y-%m-%d", "%Y%b%d"]
#
# The key XLOIL_PATH is edited by the xlOil_Install powershell script
# Note: Use [[]] syntax because the order of Environment variables matters
#
#[[Addin.Environment]]
#XLOIL_PATH=""
#[[Addin.Environment]]
#PATH='''%PATH%;%XLOIL_PATH%'''
##### Python Plugin Settings
#
[xlOil_Python]
#
# Python modules to load on start up. Must be on python's sys.path
#
LoadModules=["xloil.xloil_ribbon", "excelfunc"]
#
# On workbook open, look for a python file matching this template
# where * is replaced by the Excel workbook name
#
WorkbookModule="*.py"
#
# Look for a module matching this pattern in the directory of the
# XLL when it is loaded where '*' is replaced with the addin name.
# The default is "*.py" which means an ini file is optional for a
# simple addin
#
#AddinModule="*.py"
#
# Calls Py_SetPath before initialising python. This allows explict
# specification of python's sys.path rather than using the usual
# search routine, which overrides any setting of PYTHONPATH.
# Generally this option is not required.
#
#SetSysPath='''.'''
#
# Sets the library for COM support. This is used by the `xloil.app`
# and `to_com` functions although can be override on a per-call basis.
# The default is 'win32com', 'comtypes' is also available.
#
#ComLib="comtypes"
#
# Selects the debugger to use. The choices are:
# * Visual Studio 2019+ : no need to pre-select this, just attach to
# a running Excel process
# * VS Code: choose `vscode` or `debugpy`. The `DebugPyPort` attribute
# then determines the port on which the server listens.
# * Pdb: choose 'pdb'
#
Debugger = ""
#
# Determines the port on which the server listens.
#
DebugPyPort = "5678"
#UseLoaderThread=false
##### Python Environment
#
# We need to set the python environment paths. Usually this is done automatically
# by `xloil install` or `xloil create` or the ribbon.
#
# Usually just set PYTHONEXECUTABLE - python should be able to figure out the rest.
# xlOil also uses this variable to help detect the presence of a virtual Environment.
# Alternatively set the PYTHONPATH, PYTHONHOME and PATH directly as required by your
# distribution.
#
# Note environment vars can be set by dynamically expanding a registry key.
#
[xlOil_Python.Environment]
PYTHONCASEOK="1"
#
# If this environment variable is set, xlOil tries to load the corresponding
# pythonXY.dll as the interpreter. If unset or blank, xlOil uses the reported
# python version of the interpreter in the first python dist found.
#
XLOIL_PYTHON_VERSION=""
#
# PYTHONEXECUTABLE should point to the python.exe of the current environment.
# Note it is possible to dynamically expand registry keys contained in angle
# brackets <>.
#
PYTHONEXECUTABLE='''D:\lib\Python311-32\python.exe'''
#PYTHONEXECUTABLE=''''''
XLOIL_PYTHON_PATH='''D:\lib'''
PYTHONPATH='''%PYTHONPATH%;D:\lib'''
================================================
FILE: docs/make.cmd
================================================
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
if "%1" == "" goto help
set XLOIL_SOLN_DIR=%~dp0..
set DOC_BUILD_DIR=%XLOIL_SOLN_DIR%\build\docs
set PY_PACKAGE_DIR=%XLOIL_SOLN_DIR%\libs\xlOil_Python\Package
set SOURCEDIR=%~dp0\source
set PATH=%PATH%;C:\Program Files\doxygen\bin
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
mkdir "%DOC_BUILD_DIR%"
if "%1" == "doxygen" goto doxygen
if "%1" == "-bin" (
set XLOIL_BIN_DIR=%XLOIL_SOLN_DIR%\build\%2
shift
shift
)
REM Generate the doc stubs: since we can import the core pyd locally, this is
REM really just for ReadTheDocs
if exist "%PY_PACKAGE_DIR%\generate_stubs.py" (
echo.Generating doc stubs for xloil_core
python "%PY_PACKAGE_DIR%\generate_stubs.py"
)
REM It's very important to pass the -E argument to sphinx, otherwise it does
REM not notice changes to docstrings in python modules and generates the
REM wrong documentation
REM Set READTHEDOCS so we get consistency with the RTD online version
set READTHEDOCS=1
%SPHINXBUILD% -M %1 "%SOURCEDIR%" "%DOC_BUILD_DIR%" %SPHINXOPTS% -E -W --keep-going %O%
set READTHEDOCS=
goto end
:doxygen
pushd source
REM For some reason doxygen can't create directories itself
mkdir "%DOC_BUILD_DIR%\doxygen\html\doxygen"
mkdir "%DOC_BUILD_DIR%\doxygen\xml\doxygen"
REM Need to reverse slashes for doxygen because life is tough
set "XLO_SOLN_DIR=%XLOIL_SOLN_DIR:\=/%"
doxygen xloil.doxyfile
popd
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %DOC_BUILD_DIR% %SPHINXOPTS% %O%
:end
popd
================================================
FILE: docs/requirements.txt
================================================
numpy
numpydoc
autodocsumm
sphinx-rtd-theme
docutils>=0.16
sphinx_rtd_theme
autodocsumm
================================================
FILE: docs/source/Concepts.rst
================================================
==============
xlOil Concepts
==============
This document explains some of the key xlOil concepts shared accross different languages
.. contents::
:local:
Excel Functions (UDFs)
----------------------
Excel supports several classes of user-defined functions:
- Macros: run at user request, have write access to workbook
- Worksheet functions: run by Excel's calculation cycle. Several sub-types:
- Vanilla
- Thread-safe: can be run concurrently
- Macro-type: can read from sheet addresses and invoke a wider variety of Excel interface functions
- Async: can run asynchronously during the calc cycle, but not in the background
- RTD: (real time data) background threads which push data onto the sheet when it becomes available
- Cluster: can be packaged to run on a remote Excel compute cluster
xlOil currently supports all but Cluster functions.
Excel can pass functions / macros data in one of these types:
- Integer
- Boolean
- Floating point
- String
- Error, e.g. #NUM!, #VALUE!
- Empty
- Array of any of the above
- Range refering to a worksheet address
There is no date type. Excel's builtin date functions interpret numbers as days since 1900.
Excel does not support timezones.
.. _core-cached-objects:
Cached Excel Objects
--------------------
xlOil has an internal lookup for Excel values, which is a convenient way of
passing arrays around a sheet and as arguments to other xlOil functions.
The function ``=xloRef(A1:B2)`` returns a cache string of the form:
``[WorkbookName]SheetName!CellRef,#``
The data can be recovered using ``=xloVal()``. Alternatively,
this string can be passed instead of the source range to xlOil functions which
support cache lookups - and for large arrays this is much faster.
Example use cases are:
* Where you might use a named range - to avoid updating references
in multiple functions when data is appended. Because `xloRef` automatically
trims the range back to the last non-blank row, it can be pointed to a range
far larger than the data.
* To speed up passing the same large array into several functions
(e.g. multiple lookups from a data table, although consider xlOil_SQL if
you want to do this).
However, there is a disadvantage to using `xloRef`: the cache is cleared when
a workbook is closed, but Excel does not know to recalculate the `xloRef`
functions when the workbook is reopened. Hence you need to force a sheet
recalculation using *Ctrl-Alt-F9*.
In addition to caching arrays, xlOil plugins use the cache to opaquely return
referencs to in-memory structures. Although the strings look similar, they
point to very different objects and cannot be written to the sheet using `xloVal`.
.. _concepts-rtd-async:
Rtd / Async
-----------
In Excel, RTD (real time data) functions are able to return values independently of Excel's
calculation cycle. Excel has supported RTD functions since at least Excel 2002. In Excel
2010, Excel introduced native async functions.
RTD:
* Pro: operates independently of the calc cycle - true background execution
* Pro: provides notification when an RTD function call is changed or removed
* Con: increased overhead compared to native async
* Con: requires automatic calculation enabled (or repeated presses of F9 until calc is done)
Native async:
* Pro: Less overhead compared to RTD
* Pro: works with manual calc mode
* Con: tied to calc cycle, so any interruption cancels all asyncs functions
The last con is particularly problematic for native async: *any* user interaction with Excel will
interrupt the calc, so whilst native async functions can run asynchronously with each other, they
cannot be used to perform background calculations.
.. _concepts-ribbon:
Custom UI: The Fluent Ribbon
----------------------------
xlOil allows dynamic creation of Excel Ribbon components. The ribbon is defined by XML
(surrounded with tags) which should be created with a specialised editor, see the
*Resources* below. Controls in the ribbon interact with user code via callback handlers.
These callbacks pass a variety of arguments and may expect a return value; it is important
to check that the any callback behaves as per the callback specifications in the *Resources*.
To pass ribbon XML into Excel, xlOil creates a COM-based add-in in addition to the XLL-based
add-in which loads the xlOil core - you can see this appearing in Excel's add-in list in the
Excel's Options windows.
Resources:
* `Microsoft: Overview of the Office Fluent Ribbon `_
* `Microsoft: Customizing the Office Fluent Ribbon for Developers `_
* `Microsoft: Custom UI XML Markup Specification `_
* `Microsoft: Ribbon Callback Specifications `_
* `Office RibbonX Editor `_
* `Ron de Bruin: Ribbon Examples files and Tips `_
.. _concepts-intellisense:
Intellisense / Function Context Help
------------------------------------
To get pop-up function help, install `Excel-DNA Intellisense `_
In addition to the function wizard, Excel has a function context help which pops up as you type.
This is only directly available for built-in functions. Fortunately, Govert of *Excel-DNA* has created
an add-in which mimics this behaviour for user-defined functions. Download the XLL (with the right bitness)
from the link above and activate it by one of:
1) Dragging the XLL into Excel (must be done once per session)
2) Install it via *Excel* => *Options* => *Addins* => *Manage*.
3) Copy it to your `%APPDATA%\\Microsoft\\Excel\\XLSTART` directory.
You will now get pop-up help for all xlOil plugins functions, including any dynamically registered ones,
for example, created with python plugin.
================================================
FILE: docs/source/Core.rst
================================================
===========
xlOil Core
===========
Here we tell you how to get started using xlOil and how to distribute addins
.. _core-getting-started:
Getting Started
---------------
You need **Excel 2010** or later on a desktop PC. xlOil will not work with online
or Mac versions of Office.
.. important::
If you want to use xlOil with python you should install it via `pip`. Stop reading
this page and go to :doc:`xlOil_Python/GettingStarted`
Download the binary package (e.g. from gitlab) and unzip to a directory of
your choice.
You can run the `xlOil_Install.ps1` script to install the addin for every
Excel session, or just drop `xlOil.xll` into any running Excel session
to load xlOil temporarily.
xlOil should now load when you open Excel, try following
:any:`sql-getting-started`
To configure the plugins being loaded, see :any:`core-edit-settings-files`.
Some plugings, such as the python one, have several paths which must be set
correctly - it will generally be easier to use these plugins by following their
specific installation instructions.
.. _core-example-sheets:
Example sheets
--------------
To check your setup and see some of the capabilities of xlOil, try:
:download:`Tests and Examples `.
.. _core-edit-settings-files:
Editing the settings file
-------------------------
There is an `xlOil.ini` file linked to the main `xlOil.xll` addin. (This ini file
is actually parsed as TOML, an extension of the ini format). xlOil searches for
this file first in `%APPDATA%/xlOil` then in the directory containing the `xlOil.xll`
addin.
The two most important setting in `xlOil.ini` are:
::
...
XLOIL_PATH='''C:\lib\xloil```
...
Plugins=["xloil_Python.dll", "xlOil_SQL.dll"]
``XLOIL_PATH`` allows the `xlOil.xll` addin to locate the main xlOil DLL if the
addin is being run from a different directory. When the main DLL has loaded,
xlOil loads the specified plugins. It searches for these plugins first in the
directory containing `xlOil.dll`, then the directory containing the XLL, then
the usual DLL search paths.
Setting enviroment variables in settings files
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each plugin can have an *Environment* section in the settings file. Within this block
tokens are interpreted as enviroment variables to set. A plugin's environment settings
are processed before the plugin is loaded.
Keys are interpreted as environement variables to set. Values can reference other enviroment
variables by surrounding the name with `%` characters.
In addition you can pull values from the registry by surrounding the registry
path with angle brackets `<>`. Leaving a trailing backslash `\\` in the
registry path fetches the default value for that key.
The TOML syntax of three single quotes indicates a string literal, this avoids escaping
all the backslashes.
The default enviroment block for Python looks like this:
::
[[xlOil_Python.Environment]]
xlOilPythonVersion="3.7"
[[xlOil_Python.Environment]]
PYTHONPATH=''''''
PYTHON_LIB=''''''
[[xlOil_Python.Environment]]
PATH='''%PATH%;%PYTHON_LIB%;%PYTHON_LIB%\Library\bin'''
The double brackets tell TOML that the order of these declarations is important,
this means we can refer to previously set enviroment variables.
In addition you can pull values from the registry by surrounding the registry
path with angle brackets `<>`, for example,
``.
Leaving a trailing backslash `\\` in the registry path fetches the default
value for that key.
Troubleshooting
---------------
You may need to tweak your settings file: :any:`core-edit-settings-files`
A common problem is that the COM interface misbehaves by either failing on start-up or failing
because of an open dialog box in Excel. For a start-up fail, unload and reload the addin.
For other errors try to close dialog boxes or panes and if that fails, restart Excel.
The log file
~~~~~~~~~~~~~
If xlOil detects a serious load error, it pops up a log window to alert you (this can
be turned off). If it succesfully loaded the core DLL a log file will also be created
next to `xlOil.ini`, which by default is in ``%APPDATA%\xlOil``. If xlOil loaded, the
worksheet function `xloLog` can tell you where this file is. A setting in `xlOil.ini`
controls the log level.
Manual installation
~~~~~~~~~~~~~~~~~~~
The `xlOil_Install.ps1` script does the following:
1. Check xlOil is not in Excel's disabled add-ins
2. Copy xlOil.xll to the ``%APPDATA%\Microsoft\Excel\XLSTART`` directory
3. Copy xlOil.ini in the ``%APPDATA%\xlOil``` directory
4. Check VBA Object Model access is allowed in
`Excel > File > Options > Trust Center > Trust Center Settings > Macro Settings``
Manual removal
~~~~~~~~~~~~~~
Should you need to force remove xlOil, do the following:
1. Remove *xlOil.xll* from ``%APPDATA%\Microsoft\Excel\XLSTART``
2. Remove the directory ``%APPDATA%\xlOil```
If you have added *xlOil.xll* or another xll add-in (xlOil does not do this by default)
and you want to remove it go to:
1. `Excel > File > Options > Add-ins > Manage Excel Addins`
2. If the previous step fails to remove the addin, start Excel with elevated/admin
priviledges and retry
3. If that fails, try to remove the add-in from the registry key
``HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\\Excel\Options``.
You should see values *OPEN*, *OPEN1*, etc with add-in names to be loaded. After removing
a value, you need to rename the others to preserve the numeric sequence.
4. If that does not work, also look at this registry key:
``HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\\Excel\Options``.
Note you may need to run the registry editor with elevated priviledges.
To really scrub the registry, you may find references to the addin under:
* `HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Office\\\\Excel\\Add-in Manager`
* `HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Office\\\\Excel\\AddInLoadTimes`
* `HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Office\\\\Excel\\Resiliency\DisabledItems`
* `HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Office\\Excel\\Addins`
* `HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Office\\Excel\\AddinsData`
================================================
FILE: docs/source/Developer.rst
================================================
=======================
xlOil Development Guide
=======================
Getting started as developer
----------------------------
- You need Visual Studio 2022 or newer
- All xlOil_Core dependencies are already in the `external` folder. Some of them are compressed,
so unpack them.
- To build the `xlOil-COM` library you need the ATL headers which can be installed with the Visual
Studio installer (under C++ development).
- For debugging, set xlOil_Loader as the target project, with
command=`` args=`$(OutDir)\xloil.xll`
Release Instructions
--------------------
::
cd \tools
python stage.py
(Optional) test python wheels with
::
cd \build\staging\pypackage
pip install dist/xlOil-0.3-cp37-cp37m-win_amd64.whl
pip uninstall dist/xlOil-0.3-cp37-cp37m-win_amd64.whl
Use twine to upload to PyPI (note you need to ensure twine has the right login
keys/secrets):
::
cd \build\staging\pypackage
# (Optional test)
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
# The real thing
twine upload dist/*
================================================
FILE: docs/source/Events.rst
================================================
==================
xlOil Excel Events
==================
.. contents::
:local:
Introduction
------------
xlOil allows code to hook into Excel events. The Excel API documentation
`Excel.Application `_
contains more complete descriptions of most of these events, with the exception of the
`CalcCancelled`, `WorkbookAfterClose`, `XllAdd` and `XllRemove` events which are not part of the
*Application* object and are provided by xlOil.
For the syntax used to hook these events, see the individual language documentation.
Parameter types
===============
Most events use some of the parameter types described below:
* *Workbook name*: passed as a string (not a ``Workbook`` object)
* *Worksheet name*: passed as a string
* *Range*: passed as a ``Range`` object
* *Cancel*: a bool which starts as False If the event handler sets this argument to True,
further processing of the event will stop
AfterCalculate
--------------
This event occurs after all Worksheet.Calculate, Chart.Calculate, QueryTable.AfterRefresh,
and SheetChange events. It's the last event to occur after all refresh processing and all
calc processing have completed, and it occurs after CalculationState is set to xlDone.
This event is called if the calculation was interrupted (for example by the user pressing
a key or mouse button).
WorkbookOpen
------------
Occurs when a workbook is opened. Takes a single parameter containing the workbook name.
NewWorkbook
-----------
Occurs when a new workbook is created. Takes a single parameter containing the workbook name.
SheetSelectionChange
--------------------
Occurs when the selection changes on any worksheet (doesn't occur if the selection is on a
chart sheet). Takes two paramters:
* Worksheet name
* Target / selected Range
SheetBeforeDoubleClick
----------------------
Occurs when any worksheet is double-clicked, before the default double-click action.
Takes three parameters
* Worksheet name
* Target - A Range giving cell nearest to the mouse pointer when the double-click occurred.
* Cancel - False when the event occurs. If the event procedure sets this argument to True, the default
double-click action isn't performed when the procedure is finished.
SheetBeforeRightClick
---------------------
Occurs when any worksheet is right-clicked, before the default right-click action.
Takes three parameters
* Worksheet name
* Target - A Range giving the cell nearest to the mouse pointer when the right-click occurred.
* Cancel - False when the event occurs. If the event procedure sets this argument to True, the default
right-click action isn't performed when the procedure is finished.
SheetActivate
-------------
Occurs when any sheet is activated. Takes a single parameter containing the sheet name.
SheetDeactivate
---------------
Occurs when any sheet is deactivated. Takes a single parameter containing the sheet name.
SheetCalculate
--------------
Occurs after any worksheet is recalculated or after any changed data is plotted on a chart.
Takes a single parameter containing the sheet name.
SheetChange
-----------
Occurs when cells in any worksheet are changed by the user or by an external link. Takes
two paramters:
* Worksheet name
* Changed Range
WorkbookActivate
----------------
Occurs when any workbook is activated. Takes a single parameter containing the workbook name.
WorkbookDeactivate
------------------
Occurs when any workbook is deactivated. Takes a single parameter containing the workbook name.
WorkbookBeforeClose
-------------------
Occurs immediately before any open workbook closes. Takes two parameters
* Workbook name
* Cancel - False when the event occurs. If the event procedure sets this argument to True, the
workbook doesn't close when the procedure is finished.
The event is not called for each workbook when Excel exits.
WorkbookBeforeSave
------------------
Occurs before any open workbook is saved. Takes three parameters:
* Workbook name
* SaveAsUI - True if the Save As dialog box will be displayed due to changes made that need to
be saved in the workbook.
* Cancel - False when the event occurs. If the event procedure sets this argument to True, the
workbook isn't save when the procedure is finished.
WorkbookBeforePrint
-------------------
Occurs immediately before any open workbook is printed. Takes two parameters
* Workbook name
* Cancel - False when the event occurs. If the event procedure sets this argument to True, the
workbook doesn't print when the procedure is finished.
WorkbookAfterClose
------------------
Excel's *WorkbookBeforeClose* event is cancellable by the user so it is not possible to know if
the workbook actually closed. When xlOil calls `WorkbookAfterClose`, the workbook is certainly
closed, but it may be some time since that closure happened. Takes a single parameter containing the
workbook name.
The event is not called for each workbook when xlOil exits. This event is not part of the
*Excel.Application* API.
WorkbookNewSheet
----------------
Occurs when a new sheet is created in any open workbook. The first parameter is the workbook name,
the second is the new sheet name.
WorkbookAddinInstall
--------------------
Occurs when a workbook is installed as an add-in. Takes a single parameter containing the workbook name.
WorkbookAddinUninstall
----------------------
Occurs when any add-in workbook is uninstalled. Takes a single parameter containing the workbook name.
CalcCancelled
-------------
Called when the calculation cycle is cancelled (for example by the user pressing a key or mouse button).
Native async functions should stop any background calculation when this event is received.
This event is not part of the *Excel.Application* API.
XllAdd
------
Triggered when an XLL related to this instance of xlOil is added by the user using the Addin settings
window. The parameter is the XLL filename.
This event is not part of the *Excel.Application* API.
XllRemove
---------
Triggered when an XLL related to this instance of xlOil is removed by the user using the Addin settings
window. The parameter is the XLL filename.
This event is not part of the *Excel.Application* API.
================================================
FILE: docs/source/Introduction.rst
================================================
=====
xlOil
=====
xlOil is a framework for linking programming languages with Excel language.
That is, a way to write functions in a language of your choice and have them
appear in Excel as worksheet functions and macros, as well as manipulating
the application and GUI similar to VBA.
xlOil is designed to have very low overheads when calling your own worksheet
functions.
xlOil supports different languages via plugins. The languages currently
supported are:
- C++
- Python
- SQL
In addition there is :any:`xlOil_Utils` which contains some handy tools which Microsoft
never quite got around to adding.
You can use xlOil as an end-user of these plugins or you can use it to write
you own language bindings and contribute.
.. important::
**Start Here:** :any:`core-getting-started`
xlOil features
--------------
* Python
- Very concise syntax to declare an Excel function
- Optional type checking of function parameters
- Supports keyword arguments
- Choice of globally declared functions or code modules limited to a single workbook (just
like VBA workbook-level functions)
- Tight integration with numpy - very low overheads for array functions
- Understands python tuples, lists, dictionarys and pandas dataframes
- Async functions
- RTD functions and on-the-fly RTD server creation
- Macro type functions which write to ranges on the sheet
- Access to the Excel Application object
- Hook Excel events
- Pass any python object back to Excel and then back into any python function
- Simple and quick add-in deployment
- Two-way connection to Jupyter notebooks: run worksheet functions in Jupyter and query variables
in the jupyter kernel
- On-the-fly creation/destruction of COM addin objects and Ribbon UI
* C++
- Safe and convenient wrappers around most things in the C-API
- Concise syntax to declare Excel functions: registration is automatic
- Deal with Excel variants, Ranges, Arrays and strings in a natural C++ fashion
- Object cache allows returning opaque objects to Excel and passing them back to other functions
- Async functions
- RTD functions and on-the-fly RTD server creation
- On-the-fly creation/destruction of COM addin objects and Ribbon UI
* SQL
- Create tables from Excel ranges and arrays
- Query and join them with the full sqlite3 SQL syntax
* Utils: very fast functions to
- Sort on multiple columns
- Split and join strings
- Make arrays from blocks
Why xlOil was created
---------------------
Programming with Excel has no ideal solution. You have a choice of APIs:
- VBA - great integration with Excel, but clunky syntax compared with
other languages, few common libraries, limited IDE, testing and source
control very difficult, single-threaded, use not encouraged by MS.
- C-API - It's C and hence unsafe. The API is also old, has some quirks
and is missing many features of the other APIs. But it's the fastest
interface.
- COM - More fully-featured but slower and strangely missing some features
of the C-API. It's C++ so still pretty unsafe. Has some unexpected behaviour
and may fail to respond. Requires COM binding support in your language
or the syntax is essentially unusuable.
- .Net API - actually sits on top of COM, the best modern interface
but only for .Net languages and speed-limited by COM overheads, still missing
some C-API features.
- Javascript API - supports Office on multiple devices and operating system, but
very slow with limited functionality compared to other APIs.
xlOil tries to give you the C and COM APIs blended in a more friendly
fashion and adds:
- Solution to the "how to register a worksheet function without a static DLL entry point" problem
- Object caching
- A framework for converting excel variant types to another language and back
- A convenient way of creating worksheet-scope functions
- A loader stub
- Goodwill to all men
Comparison between xlOil and other Excel Addin solutions
--------------------------------------------------------
Given the age of Excel, there are many other solutions for Excel add-in
development.
Most available packages are commercial (and quite expensive) so I
cannot test relative performance with xlOil. They are generally
focussed on a single language rather than providing a framework
for language bindings as xlOil does.
The following, likely incomplete, list includes some of the most
prominent Excel addin software.
- Addin express: (commercial) fully-featured with nice GUI and the support
seems good as well. Limited to .Net languages, but covers the entire Office
suite (not just Excel)
- ExcelDNA: (free) mature, well-regarded and widely used, covers
almost all Excel API features, but only for .Net languages. I strongly
recommend this software if you are using .Net languages.
- XLL Plus: (commercial) seems to be fully-featured with GUI wizards
to help developers, but only for C++ and the most expensive sofware
here.
- PyXLL: (commercial) Python-only. Supports the full range of Excel
API features and some functionality to run on remote servers.
- XlWings: (mostly free) Python-only. More mature sofware, but considerably
slower (2000x in my test case) than xlOil due to use of slower APIs.
Can only create 'local' functions backed by VBA, so every Excel sheet needs
to be be a macro sheet with special VBA redirects. This means it is not viable
for addin deployment. Supports Mac and Python 2.7 (but licence fee required
for this).
================================================
FILE: docs/source/conf.py
================================================
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
from pathlib import Path
#
# The default soln_dir and bin_dir paths here are to allow VS code to auto
# preview the docs. I'm currently not sure how to add environment variables
# to processes run by VS code.
#
file_dir = Path(__file__).parent
xloil_dir = file_dir.parent.parent
soln_dir = Path(os.environ.get("XLOIL_SOLN_DIR", xloil_dir)).resolve()
bin_dir = Path(os.environ.get("XLOIL_BIN_DIR", xloil_dir / "build/x64/Debug")).resolve()
if bin_dir.exists():
sys.path.append(str(bin_dir))
sys.path.append(str(soln_dir / "libs/xlOil_Python/Package"))
# May as well fail here if we can't find xloil
try:
import xloil
except Exception as e:
raise Exception(f'{e}. SysPath={sys.path}', e)
# -- Project information -----------------------------------------------------
project = 'xlOil'
copyright = '2022, Steven Cunnane'
author = 'Steven Cunnane'
# The full version, including alpha/beta/rc tags
release = (soln_dir / "Version.txt").read_text()
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.autosectionlabel",
"numpydoc",
'autodocsumm'
]
# Avoid duplicate label warnings from autosectionlabel
suppress_warnings = ['autosectionlabel.*']
# True to prefix each section label with the name of the document it is in,
# followed by a colon. For example, index:Introduction for a section called
# Introduction that appears in document index.rst.
autosectionlabel_prefix_document = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
USE_READTHEDOCS_THEME = True
if USE_READTHEDOCS_THEME:
import sphinx_rtd_theme
extensions.append('sphinx_rtd_theme')
html_theme = "sphinx_rtd_theme"
else:
html_theme = 'bizstyle'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []# ['_static']
# A list of paths that contain extra files not directly related to the documentation,
# such as robots.txt or .htaccess. Relative paths are taken as relative to the
# configuration directory. They are copied to the output directory.
#html_extra_path = ['../build/doxygen']
autodoc_default_flags = ['members']
#
# If autosummary_generate is True, stub .rst files for for items in the autosummary
# toctree are automatically generated. We prefer to keep a flatter structure.
#
autosummary_generate = False
# See https://stackoverflow.com/questions/34216659/
numpydoc_show_class_members=False
#
# Required for readthedocs build as the master_doc seems to default to 'contents' in
# their environment. Locally sphinx builds fine without this
#
master_doc = 'index'
# -- Generate examples file ---------------------------------------------------
#
# Only do this when XLOIL_BIN_DIR is explicitly set so not on ad-hoc run
#
if "XLOIL_BIN_DIR" in os.environ:
import zipfile
from zipfile import ZipFile
try: os.makedirs('_build')
except FileExistsError: pass
zipObj = ZipFile('_build/xlOilExamples.zip', 'w', compression=zipfile.ZIP_BZIP2)
try:
zipObj.write(soln_dir / "tests" / "AutoSheets" / "PythonTest.xlsm", "PythonTest.xlsx")
zipObj.write(soln_dir / "tests" / "ManualSheets" / "python" / "PythonTestAsync.xlsm", "PythonTestAsync.xlsm")
zipObj.write(soln_dir / "tests" / "AutoSheets" / "PythonTest.py", "PythonTest.py")
zipObj.write(soln_dir / "tests" / "AutoSheets" / "TestModule.py", "TestModule.py")
zipObj.write(soln_dir / "tests" / "AutoSheets" / "TestSQL.xlsx", "TestSQL.xlsx")
zipObj.write(soln_dir / "tests" / "AutoSheets" / "TestUtils.xlsx", "TestUtils.xlsx")
except FileNotFoundError as err:
print("WARNING: Could not create xlOilExamples.zip due to: ", str(err))
zipObj.close()
================================================
FILE: docs/source/index.rst
================================================
.. raw:: html
xlOil documentation
===================
.. toctree::
:maxdepth: 4
:caption: Contents:
:glob:
Introduction
Core
Concepts
Events
xlOil_Core_Functions
xlOil_Cpp/index
xlOil_Python/index
xlOil_SQL/index
xlOil_Utils
Developer
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
================================================
FILE: docs/source/xlOil_Core_Functions.rst
================================================
=========================
xlOil Core Functions
=========================
This page describes the worksheet functions registered by the xlOil core, which should always
be available when xlOil is running.
xloRef: adds a value or array to the cache
------------------------------------------
.. function:: xloRef(arrayOrRange)
Adds the specified value or range or array to the object cache and returns a string
reference.
See :any:`core-cached-objects`
xloVal: Retrives a value from the Excel data cache
--------------------------------------------------
.. function:: xloVal(cacheRefString)
Given a reference string, returns a stored array or value. Cached values
are not saved with the workbook so will need to be recreated by forcing
a full recalc (press Ctrl-Alt-F9) when the workbook is opened
See :any:`core-cached-objects`
xloHelp: returns help on an xlOil registered function
------------------------------------------------------
.. function:: xloHelp(functionName)
Returns a two column array containing the help information passed to Excel's
function wizard when the function was registered. The first row contains
the function name and the main help string. Subsequent rows contain argument
names and their associated help.
In the function wizard all help strings are limited to 256 characters. xloHelp
does not have this limitation.
xloLog: flushes the log file and returns its location
-----------------------------------------------------
.. function:: xloLog(ShowWindow=FALSE)
The xlOil log is only flushed (written to file) occasionally or when an error
or warning occurs. Executing this function flushes the log and returns the
location of the log file (by default this is the same directory as the settings
file).
Setting the *ShowWindow* parameter causes xlOil's log window to appear. This
doesn't display the contents of the entire log file but only the most recent log
messages.
xloVersion: returns information on the xlOil version
----------------------------------------------------
.. function:: xloVersion()
Returns a two-column array of version information.
================================================
FILE: docs/source/xlOil_Cpp/COM.rst
================================================
=======================
xlOil C++ Using COM
=======================
The root of Excel's COM interface is `Excel.Application `_
which VBA programmers will be familiar with.
The easiest way to work with it in C++ is by importing the type library into your source file -
this allows compile time checks and auto-completion and results in faster code. COM objects
can be accessed using 'late-binding' where methods and properties are queried from the object
at runtime, but this clearly limits performance.
In xlOil, the appropriate type library can be imported with the *ExcelTypeLib.h* header:
::
#include
#include
using namespace xloil;
XLO_FUNC_START(testToday())
{
excelApp().Range[L"A1"]->NumberFormat = L"dd-mm-yyyy";
}
XLO_FUNC_END(testToday).macro();
If *ExcelTypeLib.h* is included before `xlOil.h` it adds COM error handling to any worksheet
functions declared with `XLO_FUNC_START`. This is useful because COM throws its own `_com_error`
classes which do not derive from `std::exception`.
I cannot find a specific Excel/COM/C++ tutorial at this time, but the *Excel.Application* API
documentation is comprehensive.
================================================
FILE: docs/source/xlOil_Cpp/CppRtd.rst
================================================
=======================
xlOil C++ RTD functions
=======================
In Excel, RTD (real time data) functions are able to return values independently of Excel's
calculation cycle. The classic example of this is a stock ticker with live prices. This is contrasted
with Excel's native async support in :any:`concepts-rtd-async`.
Example: Subscription
---------------------
xlOil wraps the RTD server implementation to make subscription and publication straightfoward as in the
following example:
.. highlight:: c++
::
#include
using namespace xloil;
IRtdServer& myRtdServer()
{
static shared_ptr ptr = newRtdServer();
static auto backgroundTask = std::async([]()
{
// Somehow fetch live prices here
ptr->publish("SP500", ExcelObj(123));
ptr->publish("FTSE100", ExcelObj(456));
});
return *ptr;
}
XLO_FUNC_START(
getStock(const ExcelObj& ticker)
)
{
auto value = myRtdServer().subscribe(tag.toString().c_str());
return returnValue(value
? *value
: Const::Error(CellError::NA));
}
XLO_FUNC_END(getStock);
Example: Background task
------------------------
The RTD mechanism can be used to run slow tasks like data fetches or calculations in the background whilst
the user continues to interact with Excel. The following demonstrates a simple counter which increments
stepwise.
.. highlight:: c++
::
#include
using namespace xloil;
struct Counter : public RtdAsyncTask
{
Counter(int iStep) : _iStep(iStep) {}
int _iStep;
// This function arranges for the work to be done in the background, but must
// exit when the `RtdNotifier` requests it
std::future operator()(RtdNotifier notify) override
{
return std::async([=, step = _iStep]()
{
int _count = 0;
while (!notify.cancelled())
{
notify.publish(ExcelObj(_count));
std::this_thread::sleep_for(std::chrono::seconds(2));
_count += step;
}
});
}
// When the cell which generated the task is recalculated, xlOil cannot tell
// whether Excel is calling it with new inputs or RTD triggered the call to get
// a result. It uses this equality operator to determine if it already has a
// running task for the given set of inputs
bool operator==(const IRtdAsyncTask& that_) const override
{
const auto* that = dynamic_cast(&that_);
if (!that)
return false;
return _iStep == that->_iStep;
}
};
XLO_FUNC_START(
rtdCounter(const ExcelObj& step)
)
{
auto value = rtdAsync(
std::make_shared(step.toInt(1)));
return returnValue(value ? *value : CellError::NA);
}
XLO_FUNC_END(rtdCounter);
================================================
FILE: docs/source/xlOil_Cpp/CustomGUI.rst
================================================
==============================
xlOil C++ GUI Customisation
==============================
.. contents::
:local:
xlOil allows dynamic creation of Excel Ribbon components and custom task panes. See
:any:`concepts-ribbon` for background. The code differs slightly depending on whether
you are writing a static XLL or an xlOil plugin.
Creating a ribbon in a static XLL
---------------------------------
.. highlight:: c++
::
void ribbonHandler(const RibbonControl& ctrl) {}
struct MyAddin
{
std::shared_ptr theComAddin;
MyAddin()
{
// We need this call so that xlOil defers execution of the ribbon creation
// until Excel's COM interface is ready
xllOpenComCall([this]()
{
auto mapper = [=](const wchar_t* name) mutable { return ribbonHandler; };
theComAddin = makeAddinWithRibbon(
L"TestXlOil",
LR"(
...
)",
mapper);
});
}
};
XLO_DECLARE_ADDIN(MyAddin);
Creating a ribbon in an xlOil plugin
------------------------------------
.. highlight:: c++
::
#incude
void ribbonHandler(const RibbonControl& ctrl) {}
std::shared_ptr theComAddin;
XLO_PLUGIN_INIT(xloil::AddinContext* ctx, const xloil::PluginContext& plugin)
{
xloil::linkLogger(ctx, plugin);
auto mapper = [=](const wchar_t* name) mutable { return ribbonHandler; };
theComAddin = makeAddinWithRibbon(
L"TestXlOil",
LR"(
...
)",
mapper);
return 0;
}
Creating a Custom Task Pane
---------------------------
After a COM addin has been created using the ribbon examples above, a custom task pane
can be added in the following way.
.. highlight:: c++
::
std::shared_ptr taskPane(theComAddin->createTaskPane(L"xloil"));
taskPane->setVisible(true);
An option progid can be passed to `createTaskPane` to create a specific COM object
as the root of the task pane, if omitted, xlOil uses a simple default object.
================================================
FILE: docs/source/xlOil_Cpp/DynamicRegistration.rst
================================================
==============================
xlOil C++ Dynamic Registration
==============================
To support registration of functions in other languages, xlOil has the ability to register worksheet
functions generated at runtime. In C++, you usually can create a static entry point for each function
you want to register using xlOil macros. These macros end up calling `Excel12(xlfRegister, ...)` and
pass the name of your function. Dynamic registration is a little trickier since `xlfRegister` requires
the name of an entry point into your XLL rather than a function pointer. xlOil arranges for such a
suitable entry point to exist by generating a small trampoline function in assembler which redirects
to your function. However, this creates a tiny performance overhead (typically around 8-16 instructions
increasing with the number of arguments).
.. highlight:: c++
::
auto regId = RegisterLambda<>(
[](const ExcelObj& arg1, const ExcelObj& arg2)
{
...
return returnValue(...);
})
.name("Foobar")
.help("Does nothing")
.arg("Arg1")
.registerFunc();
The lambda's can take as many `const ExcelObj&` args as required. It must return `ExcelObj*`,
but it may throw: xlOil will return the error string. By specifying `const FuncInfo&` as the
type of the first argument, the callable will be given a reference to the function registration info
in addition the arguments passed by Excel.
You can dynamically register any function you could register statically, so :any:`SpecialArgs`
arguments are valid, as well as a void return, for example:
.. highlight:: c++
::
auto regId = RegisterLambda(
[](const FuncInfo&, const ExcelObj& arg1, const AsyncHandle& handle)
{
handle.returnValue(...);
})
.name("AsyncFoobar").registerFunc();
The returned register Id is a `shared_ptr` whose destructor unregisters the function, so this must be
kept in scope.
================================================
FILE: docs/source/xlOil_Cpp/Events.rst
================================================
================
xlOil C++ Events
================
See :ref:`Events:Introduction`.
Examples
--------
.. highlight:: c++
::
// When the returned shared_ptr is destroyed, the handler is unhooked.
auto ptr = xloil::Event::CalcCancelled().bind(
[this]() { this->cancel(); }));
// The returned id can be used to unhook the handler
static auto id = Event::AfterCalculate() += [logger]() { logger->flush(); };
================================================
FILE: docs/source/xlOil_Cpp/GettingStarted.rst
================================================
=========================
xlOil C++ Getting Started
=========================
There are a few ways to build a C++ project with xlOil:
1. xlOil plugin: writing a DLL which can be loaded by xlOil after the loader XLL
starts up the application. This allows co-operation with other xlOil features such as
the the object cache and settings file. It means that cache references created by
`xloRef` can be used in your addin.
2. XLL dynamically linking xlOil. This allows co-operation with the Excel object cache.
Your XLL will need to be able to find xlOil when it loads - this may be easier if delay
loading is employed.
3. XLL statically linking xlOil. This gives a standalone XLL. It does not include
the xlOil cache functions `xloRef` and `xloVal`, although it is easy to define a private
object cache with your own function names.
The header files and `xlOil.lib` file required for dynamic linking are included in the
`binary releases `_.
You need:
* `xlOil.lib`: linking library
* `xlOil.dll`: main binary
* `xlOil.xll`: XLL loader stub
* `xlOil.ini`: Main settings file
* `include/xloil`: header files
The static libs are a lot larger, so you'll need to build them from source.
If you want to read settings from the add-in's *.ini* file using *tomlplusplus* you can find
this in `xlOil/external` - it's header-only.
Creating an xlOil Plugin
~~~~~~~~~~~~~~~~~~~~~~~~
This method assumes you already have a running install of xlOil.
Create a project & solution for your DLL.
Building an xlOil plugin requires a number of build settings and the easiest way to get these right
it is use a property sheet. First, include an edited copy of `./libs/SetRoot.props` where *xlOilRoot*
points to the xlOil directory. Then include `./libs/xlOilPlugin.props`.
Now add the following source file:
.. highlight:: c++
::
#include
XLO_PLUGIN_INIT(xloil::AddinContext* ctx, const xloil::PluginContext& plugin)
{
xloil::linkLogger(ctx, plugin);
return 0;
}
using namespace xloil;
XLO_FUNC_START(
MyFunc(const ExcelObj* arg1, const ExcelObj* arg2)
)
{
auto result = arg1->toString() + ": " + arg2->toString(L"default value");
return ExcelObj::returnValue(result);
}
XLO_FUNC_END(MyFunc).threadsafe()
.help(L"Joins two strings")
.arg(L"Val1", L"First String")
.arg(L"Val2", L"Second String");
Declaring an `XLO_PLUGIN_INIT` entry point lets xlOil know the DLL is a plugin. Calling `linkLogger`
links the *spdlog* to the main xlOil log output. This is optional if you don't plan on using any logging
functionality.
The ``ExcelObj`` class wraps the xloper12 variant type used in the XLL interface. It provides
a number of accessors with a focus on efficiency and avoidance of copying (see below).
To load your plugin in Excel you need to ensure that the main xlOil add-in can locate your DLL.
You can do this by changing the Addin enviroment in the `xlOil.ini` file or changing the
current working directory or system PATH, or by simply ensuring your DLL is in the same directory
as *xlOil.dll*.
You should also add your plugin to the `Plugins=` key in `xlOil.ini`.
Creating an XLL which statically links xlOil
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Build xlOil with the *DebugStatic* or *ReleaseStatic* configuration.
Create a project & solution for your XLL. It should output a DLL, but change the *Target Extension* to XLL.
Building an xlOil-linked XLL requires a number of build settings and the easiest way to get these right
it is use a property sheet. First, include an edited copy of `./libs/SetRoot.props` where *xlOilRoot*
points to the xlOil directory. Then include `./libs/xlOilStaticLib.props`.
Alternatively, you can copy and modify the `TestAddin` project, though you will still need to ensure
*xlOilRoot* is set correctly.
Create a `Main.cpp` file with the following contents:
.. highlight:: c++
::
#include
#include
using namespace xloil;
struct MyAddin
{};
XLO_DECLARE_ADDIN(MyAddin);
XLO_FUNC_START(
MyFunc(const ExcelObj* arg1, const ExcelObj* arg2)
)
{
auto result = arg1->toString() + ": " + arg2->toString(L"default value");
return ExcelObj::returnValue(result);
}
XLO_FUNC_END(MyFunc).threadsafe()
.help(L"Joins two strings")
.arg(L"Val1", L"First String")
.arg(L"Val2", L"Second String");
Debugging your addin
~~~~~~~~~~~~~~~~~~~~
Start a debugging session with the following settings where the XLL may be `xlOil.xll`
or one you built yourself depending on which path you are following.
* Command:
* Arguments:
Setting the working directory is optional, but it may help locate any externals DLLs your
add-in uses.
There are many examples to follow in the ``xloil_Utils`` and ``xloil_SQL`` projects.
================================================
FILE: docs/source/xlOil_Cpp/ObjectHandles.rst
================================================
========================
xlOil C++ Object Handles
========================
It is often convenient to pass a pointer to an in-memory object from one function to another in a way
which generalises :any:`core-cached-objects`. xlOil makes this very straightforward:
.. highlight:: c++
::
XLO_FUNC_START(
cacheOut(const ExcelObj& val)
)
{
auto key = makeCached(val.toInt());
return returnValue(key);
}
XLO_FUNC_END(cacheOut);
XLO_FUNC_START(
cacheIn(const ExcelObj& cacheKey)
)
{
auto* val = getCached(cacheKey.asPString());
return returnValue(val ? *val : 0);
}
XLO_FUNC_END(cacheIn);
The cache key or handle returned looks `[Book1]Sheet1!R1C1,A`. The leading character
allows rapid rejection of invalid cache strings; it is unique per cached object type.
Cached objects do not persist when a spreadsheet is closed, but Excel does not know to recalculate
the cells which generate the cache handles when a spreadsheet is opened, so a full recalc must
be manually performed with Ctrl-Alt-F9. To avoid this requirement, you can make the functions which
generate the handles RTD, but this carries some overhead.
================================================
FILE: docs/source/xlOil_Cpp/SpecialArgs.rst
================================================
======================
XLL Special Arguments
======================
User-defined Excel functions in XLLs usually take a number of `const ExcelObj&` arguments
and return a `ExcelObj*`. xlOil supports a number of other possibilities, the most useful
of which are `RangeArg` and `AsyncHandle`.
RangeArg
--------
Declaring a class as `const RangeArg&` instead of an `ExcelObj` in registered function tells
xlOil to allow range references to be passed. These are sheet addresses which avoid copying
the underlying data. Normally, references to areas on the sheet are converted to arrays
before being passed to the function. A `RangeArg` only allows the possibility that a range
reference be specified, `RangeArg` inherits from `ExcelObj` and so may actually be any Excel
type.
AsyncHandle
-----------
Declaring an argument as `const AsyncHandle&` causes the function to be declared as native
async type to Excel. Such a function does not return a value directly but invokes the
`returnValue` method on its handle to pass a value to Excel (which should only be done once).
ExcelObj pointer
----------------
You may opt to receive a `const ExcelObj*` instead of a reference. This is purely preference
and has no other impact.
FPArray
-------
An `FPArray` argument is a two-dimensional array of double. This is naturally very fast to
access, particulary when combined with inplace returns but has a significant drawback: if
if any value in the array passed to the function is not a number, Excel will return *#VALUE!*
without actually invoking the function.
Inplace returns
----------------
Excel supports returning values by modifying arguments in place. This is most useful for the
FPArray type. To declare inplace return, simply make the argument a non-const ref or pointer.
It is also possible to return an `ExcelObj` in-place but this is disabled by default.
In the words of the XLL SDK:
"Excel permits the registration of functions that return an XLOPER by modifying
an argument in place. However, if an XLOPER argument points to memory, and the
pointer is then overwritten by the return value of the DLL function, Excel can
leak memory. If the DLL allocated memory for the return value, Excel might try
to free that memory, which could cause an immediate crash. Therefore, you should
not modify XLOPER/XLOPER12 arguments in place."
In practice, it can be safe to modify an ExcelObj in place, for instance *xloSort*
modifies its input by changing the row order in the array, but without changing memory
allocation. However it does not use an inplace return because the drawback of this
technique is that you cannot return a different type to the one passed, in particular
you cannot return an error message or error type.
To enable inplace `ExcelObj` returns, define the macro
`XLOIL_UNSAFE_INPLACE_RETURN` before including any xlOil headers.
================================================
FILE: docs/source/xlOil_Cpp/StaticXLLs.rst
================================================
======================
xlOil C++ Static XLLs
======================
As explained in :any:`GettingStarted`, it is possible to create an XLL which statically links xlOil.
This allows an all-in-one XLL without worrying about DLL search paths. We expand on the example
in :any:`GettingStarted` to illustrate some of the other static XLL features:
.. highlight:: c++
::
#include
#include
using namespace xloil;
struct MyAddin
{
MyAddin()
{
// This constructor is called by Excel's AutoOpen
xllOpenComCall([this]()
{
// If we need to do some COM stuff on startup, like register a Ribbon,
// it needs to go in this delayed COM callback.
});
}
~MyAddin()
{
// This destructor is called by Excel's AutoClose
}
static wstring addInManagerInfo()
{
// The string returned here is displayed in Excel's addin options window
// Note the function is static: it can be called before AutoOpen.
return wstring(L"My Addin Name");
}
};
XLO_DECLARE_ADDIN(MyAddin);
================================================
FILE: docs/source/xlOil_Cpp/index.rst
================================================
======================
xlOil C++
======================
xlOil C++ provides a layer above the `XLL API `_
and includes features not available via the XLL API, such as RTD Servers, Ribbon customisation
and event handling.
The xlOil C++ interface has a `doxygen API description <../../doxygen/html/doxygen/index.html>`_.
.. toctree::
:maxdepth: 4
:caption: Contents
GettingStarted
SpecialArgs
Events
ObjectHandles
CustomGUI
CppRtd
DynamicRegistration
StaticXLLs
COM
Quick Tour of the Key Classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ExcelObj - An XLOPER wrapper
----------------------------
An *ExcelObj* is a friendly wrapper around Excel's *xloper12* struct which is the variant type
used to pass arguments in the XLL interface. An *ExcelObj* it has the same size and *xloper12* so
you can freely cast between the two (although be aware that *ExcelObj* has a destructor).
*ExcelObj* supports a number of variant-like operations: it can be compared to strings or numbers,
converted to a string, and created from values or initialiser lists. However, it's not recommended
to use *ExcelObj* as a generic variant type - reserve it for interaction with the XLL API.
ExcelArray / ArrayBuilder
-------------------------
To **view** a suitable `ExcelObj` as an array, pass it to the `ExcelArray` constructor.
By default, the array is 'trimmed' to last row and column containing data (i.e. not empty or
N/A). `ExcelArray` is a view - it is lightweight but does not own the underlying
data.
The `ArrayBuilder` class constructs arrays to return to Excel. For efficiency (of the CPU not
the programmer!) it requires you to know up-front how many array elements you require and the
total length of all strings in the array. This may mean you need to make two passes of your
data, but it saves iterating through the array on destruction.
PString
-------
Strings in the XLL API are Pascal wide char strings: the first character is the length and
they are not null terminated. `PString` wraps such a string and provides a similar interface to
`std::wstring`. There's also a `PStringView` type like `std::wstring_view`.
Range / RangeArg / ExcelRef
---------------------------
There are few types of range class with slightly different internals and usages. All behave
in a "range-like" way, supporting:
* `(i, j)` - accessor to pick out individual elements
* `range(fromRow, fromCol, toRow, toCol)` - create a sub-range
* `value()` - convert to an `ExcelObj` single value or array
* `address()` - fetch the sheet address as a string
* `set(val)` - sets the range values to the given `ExcelObj` (which may be an array)
The `RangeArg` type should only be used to declare an argument to a user-defined worksheet
function. Specifing this type means that xlOil is allowed to pass range references to your
function. See :any:`SpecialArgs`. A `RangeArg` is not directly constructable - you should
convert it to an `ExcelRef` to copy and pass it around.
The `ExcelRef` type is equivalent to an *xltypeRef* `ExcelObj`, that is it points to a
rectangular space on a specific sheet. It can be created from an `ExcelObj` which is a
range reference (typically via `RangeArg.toExcelRef()`) or a sheet address string.
The `Range` type is a virtual base: it may point either to an `ExcelRef` range or a COM
range object. Prefer `ExcelRef` when you are sure the range information has been passed via
an XLL worksheet function.
callExcel - calling XLL API functions
-------------------------------------
The XLL SDK docs describe a number of API calls beginning with `xl` such as `xlCoerce` and
`xlSheetNm`. There are also many more poorly documented API calls beginning with `xlc` and
`xlf`. They can all be found in `xlcall.h`. Invoking these functions is straightforward:
.. highlight:: c++
::
ExcelObj result = callExcel(msxll::xlSheetId, "Sheet1");
All arguments are automatically converted to `ExcelObj` type where a conversion is possible
before being passed to Excel. The type of the return value is given in the documentation.
runExcelThread - calling blocked API functions
----------------------------------------------
Excel's COM API must be called on the main thread, since it's single-threaded apartment.
Breaking this rule may lead to undefined behaviour. In addition, the COM interface must be 'ready'
which means no dialog boxes are open or other tasks which busy the COM interface.
Calls to the XLL API generally also require the main thread (with a few exceptions) and being in
the correct 'context', that is being in a function invoked by Excel.
The `runExcelThread` function queues a message to a hidden window or an asynchronous procedure call
(APC) callback which will be executed on the main thread when Excel passes control to windows
or pumps its message loop respectively (a window message is the default, most responsive choice).
The `runExcelThread` function can retry calls, trying to wait until the COM interface is ready (sadly
there is no mechanism to allow Excel to inform applications when it is ready to work, you just
have to keep trying).
In addition, setting the `XLL_API` flag runs the callback in the XLL context.
================================================
FILE: docs/source/xlOil_Python/BuiltInUDFs.rst
================================================
==================================
xlOil Built-in Worksheet Functions
==================================
The the important ``xloRef`` and ``xloVal`` functions are described at
:ref:`core-cached-objects`
xloPyLoad: import and scan a python module (worksheet function)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. function:: xloImport(ModuleName:str, From=None, As=None)
Loads functions from a module and registers them in Excel. The functions do not have to be decorated.
Provides an analogue of `from X import Y as Z`
ModuleName:
A module name or a full path name to a target py file. If empty, the workbook module
with the same name as the calling workbook is (re)loaded.
From:
If omitted, imports the specified module as normal, i.e. :any:`xloil.scan_module` If a
value or array, registers only the specified object names. If "*", all objects are registered.
To:
Optionally specifies the Excel function names to register in the same order as `From`.
Should have the same length as `From`.
See :any:`xloil.import_functions`
.. function:: xloAttr(Object, Name:str, *Args, **Kwargs)
Returns the named attribute value, or the result of calling it if possible. ie, ``object.attr``
or ``object.attr(*args, *kwargs)`` if it is a callable.
Object:
The target object
Name:
The name of the attribute to be returned. The attribute can be a bound method,
member, property, method, function or class
Args
If the attribute is callable, it will be called using these positional arguments
Kwargs
If the attribute is callable, it will be called using these keyword arguments
.. function:: xloAttrObj(Object, Name:str, *Args, **Kwargs)
Returns the value of named attribute or the result of calling the attribute. Behaves like
`xloAttr` except always returns a cache object (see :ref:`xlOil_Python/TypeConversion:Cached Objects`).
This function is useful to stop the default conversion to Excel, for example when returning
an iterable. A typical use is when chaining `=xloAttrObj` calls.
.. function:: xloPyDebug(Debugger)
See :ref:`xlOil_Python/Debugging:Selecting the debugger programmatically`
================================================
FILE: docs/source/xlOil_Python/Concepts.rst
================================================
=========================
xlOil Python Concepts
=========================
.. contents::
:local:
Workbook Modules
----------------
When an Excel workbook is opened, xlOil tries to load the module `.py`
(this is configurable). When registering functions from workbook modules, xlOil defaults
to making any declared functions :ref:`xlOil_Python/Functions:Local Functions`
The function :any:`xloil.linked_workbook` when called from a workbook module retrieves
the associated workbook path.
Array Functions
---------------
By default, xlOil-Python converts Excel array arguments to numpy arrays. The conversion
happens entirely in C++ and is very fast. Where possible you should write functions
which support array processing (vectorising) to take advantage of this, for example
the following works when it's arguments are arrays or numbers:
::
@xlo.func
def cubic(x, a, b, c, d)
return a * x ** 3 + b * x ** 2 + c * x + d
You can take this further to evaluate polynominals of n-th degree:
::
@xlo.func
def poly(x, coeffs: xlo.Array(float)):
return np.sum(coeffs * x[:,None] ** range(coeffs.T.shape[0]), axis=1)
Specifing that we expect an array argument and the data type of that array avoids the
overhead of xlOil determining the type. There is a problem with this function:
what happens if ``x`` is two-dimensional? To avoid this possibility we can specify:
::
@xlo.func
def poly(x: xlo.Array(dims=1), coeffs: xlo.Array(float)):
return np.sum(coeffs * X[:,None] ** range(coeffs.T.shape[0]), axis=1)
Events
------
Events allow for a callback on user interaction. If you are familiar with VBA, you may have used
Excel's event model already. Most of the workbook events described in
`Excel.Appliction `_
are available in xlOil.
See :ref:`xlOil_Python/ModuleReference:Events` for more details on python events and :doc:`Events`
for a description of the available Excel events.
Excel events do not use return values. However, some events take reference parameters.
For example, `WorkbookBeforeSave` has a boolean `cancel` parameter. Setting this to True cancels the
save. As references to primitive types aren't supported in python, in xlOil you need to set this
value using `cancel.value=True`.
Event handlers are (currently) global to the Excel instance, so you may need to filter by workbook name
when handling events even if you have hooked the event in a local workbook module.
Examples
~~~~~~~~
::
def greet(workbook, worksheet):
xlo.Range(f"[{workbook}]{worksheet}!A1") = "Hello!"
xlo.event.WorkbookNewSheet += greet
Registering functions in other modules
--------------------------------------
xlOil automatically scans modules when they are imported or reloaded via a
hook in python's import mechanism. This ensures any :any:`xloil.func`
decorated functions are registered.
If you load a module outside the normal ``import`` mechanism, you can tell
xlOil to look for functions to register with :any:`xloil.scan_module`.
Also see :any:`xlOil_Python/Functions:Dynamic Registration`, which explains
how any python callable can be registered as an Excel function.
Multiple addins and event loops
-------------------------------
*xlOil_Python* can be used by multiple add-ins, that is, more than one XLL
loader with its own settings and python codebase can exist in the same Excel
session.
* Each add-in / XLL is loaded in a background thread equipped with an `asyncio`
event loop. Get the loop using :any:`xloil.get_event_loop`.
* You can find the addin associated with the currently running code with
:any:`xloil.source_addin` .
* All add-ins share the same python interpreter
* All add-ins share the python object cache
* Worksheet functions are executed in Excel's main thread or one of its
worker threads for thread safe functions
* Async / RTD worksheet functions are executed in a dedicated xlOil Core
event loop which you can access with ``xloil.get_async_loop()``
* You can ask xlOil to create a separate thread & event loop for an addin.
Although CPython supports subinterpreters, most C-based extensions, particularly
*numpy* do not, so there are no plans to add subinterpreter support at this stage.
================================================
FILE: docs/source/xlOil_Python/CustomGUI.rst
================================================
==============================
xlOil Python GUI Customisation
==============================
.. contents::
:local:
Status Bar
----------
Possibly the simplest Excel GUI interaction: writing messages to Excel's status bar:
::
from xloil import StatusBar
with StatusBar(1000) as status:
status.msg('Doing slow thing')
...
status.msg('Done slow thing')
The :any:`xloil.StatusBar` object clears the status bar after the specified number of milliseconds
once the `with` context ends
Ribbon
------
xlOil allows dynamic creation of Excel Fluent Ribbon components. See :any:`concepts-ribbon` for
background.
::
gui = xlo.ExcelGUI(r'''....''',
funcmap={
'onClick1': run_click,
'onClick2': run_click,
'onUpdate': update_text,
})
The ``gui`` object :any:`xloil.ExcelGUI` holds a handle to a COM addin created to support
the ribbon customisation. If the object goes out of scope and is deleted by python or if you call
``gui.disconnect()``, the add-in is unloaded along with the ribbon customisation and any custom task
panes.
The ``funcmap`` dictionary (or function) links callbacks named in the ribbon XML to python functions.
Each handler should have a signature like the following:
::
def ribbon_button_press(ctrl: RibbonControl)
...
def ribbon_callback2(ctrl: RibbonControl, arg1, arg2)
...
def ribbon_get_text_label(ctrl: RibbonControl, *args)
return "something"
async def ribbon_callback4(ctrl: RibbonControl, *args)
...
The ``ctrl`` argument points to the control which raised the callback. The number of additional
arguments is callback dependent. Some callbacks are expected to return a value.
See `Customizing the Office Fluent Ribbon `_
for a description of the appropriate callback signature.
.. note::
Callbacks are executed in Excel's main thread unless declared *async*, in which case they will be
executed in the addin's event loop. Async callbacks cannot return values.
Instead of a dictionary, the `funcmap` object can be a function which takes any string and returns a
callback handler.
See :doc:`ExampleGUI` for an example of ribbon customisation, or look at xlOil's own ribbon in the
`xloil/xloil_ribbon.py` module.
Setting button images
=====================
Any `getImage` callbacks must return a `PIL Image `_.
This is converted to an appropriate COM object by xlOil. Instead of using a separate `getImage` callback
per control, a single `loadImage` attribute can be added:
::
...
The `MyImageLoader` function will be called with the argument `icon.jpg` and be expected to return
a *PIL Image*.
Custom Task Panes
-----------------
`Custom task panes `_ are user
interface panels that are usually docked to one side of a window in the Excel application. They can
contain a *Qt*, *Tk* or *wx* interface, or any suitable custom COM control.
Custom task panes are created using the :any:`xloil.ExcelGUI` object. There is no need to create a ribbon
as well, but task panes are normally opened using a ribbon button, because Excel does not provide a
default way for users to show or hide custom task panes.
Custom task panes are associated with a document frame window, which presents a view of a workbook
to the user. If you want to display a custom task pane with multiple workbooks, create a new instance
of the custom task pane when the user creates or opens a workbook. To do this, either handle the
`WorkbookOpen` event, or require the user to press a ribbon button to open a task pane for the active
workbook.
Thread-safety
=============
The :any:`xloil.ExcelGUI` object and custom task panes can be created in any thread (internally they
re-direct calls to Excel's main thread). Typically GUI creation will be done on xlOil's python loader
thread, which also contains an *asyncio* event loop. The individual GUI toolkits are generally not
thread-safe and should only be accessed from dedicated threads which xlOil creates. This is described
below per toolkit in more detail.
.. caution:
If another non-xlOil Excel addin uses the same GUI toolkit, it is very likely that Excel will crash.
Qt Custom Task Panes
====================
Qt support uses *qtpy* which auto-detects the Qt bindings (PySide/PyQt) and standardises the
small syntactic differences between the libraries.
.. caution::
You *must* import :any:`xloil.gui.qtpy` before any other use of Qt. This allows xlOil
to create and the *QApplication* on its own thread.
It's common in Qt GUIs to inherit from `QWidget`, so xlOil allows you to create a pane
from a `QWidget`:
::
import xloil.gui.qtpy
from qtpy.QtWidgets import QWidget
class QtTaskPane(QWidget):
def __init__(self):
super().__init__() # Don't forget this!
... # some code to draw the widget
def send_signal(self, int):
... # some code to emit a Qt signal
excelui = xlo.create_gui(...)
pane = excelui.attach_pane('MyPane', pane=QtTaskPane)
# The widget is in the pane's `widget` attribute
pane.widget.send_signal(3)
The :any:`xloil.ExcelGUI.attach_pane` call creates a task pane with the specified name. If ``pane``
is a *type* which inherits from `QWidget`, it is constructed (on the Qt thread, see below)
and placed in a :any:`xloil.gui.qtpy.QtThreadTaskPane` then attached to the Excel window.
To talk to your widget, it's best to set up a system of Qt
`signals `_ as these are thread-safe.
(Note the `syntax differs slightly in PyQt5 `_
but it is standardised by *qtpy*)
Qt Thread-safety
________________
All *Qt* interactions other than signals must take place in the same thread, or Qt
will abort. xlOil creates a special Qt thread which runs the Qt event loop, and
constructs any task panes on that thread.
To run commands on xlOil's *Qt* thread, use the :any:`xloil.gui.qtpy.Qt_thread` object
::
from xloil.gui.qtpy import Qt_thread
future = Qt_thread().submit(func, *args) # returns a concurrent.futures.Future
future2 = Qt_thread().submit_async(func, *args) # returns an asyncio.Future
future.result() # Blocks if result is required now
You can also use `Qt_thread` as a decorator to wrap the function in a `submit` call, for example:
::
@Qt_thread
def some_func():
...
Tkinter Custom Task Panes
=========================
We create a class which derives from :any:`xloil.gui.tkinter.TkThreadTaskPane` (which in turn
derives from :any:`xloil.gui.CustomTaskPane`). Unlike Qt, it's not (I think) as common to derive
from a *tkinter.Frame* object.
We draw the window into the *tkinter.Toplevel* contained in `self.top_level`.
::
from xloil.gui.tkinter import TkThreadTaskPane, Tk_thread
class TkTaskPane(TkThreadTaskPane):
@Tk_thread
def set_x(self, x):
...
def __init__(self):
super().__init__() # Don't forget this!
# This name is picked up by ExcelGUI.attach_pane
self.name = "MyPane"
import tkinter as tk
top_level = self.top_level
# Draw into window
...
excelui = xlo.ExcelGUI(xml=..., funcmap=...)
pane = excelui.attach_pane(TkTaskPane())
pane.set_x(3)
As *tkinter* does not have thread-safe signals, although it does have events which could be used here,
but for illustration, we ensure `set_x` is run on the *Tk* thread, by decorating it with
:any:`xloil.gui.tkinter.Tk_thread`. The `__init__` method is always called on the *tkinter* thread
so we don't need to decorate it.
Tkinter Thread-safety
_____________________
The :any:`xloil.gui.tkinter.Tk_thread` function behaves the same as `Qt_thread` described
in :ref:`xlOil_Python/CustomGUI:Qt Thread-safety`.
wxPython Custom Task Panes
==========================
It's common in wx GUIs to inherit from `wx.Frame`, so xlOil allows you to create a pane
from a `wx.Frame`:
::
from xloil.gui.wx import wx_thread
import wx
class OurWxPane(wx.Frame):
def __init__(self):
super().__init__(None, title='Hello')
...
@wx_thread
def set_progress(self, x: int):
...
excelui = xlo.create_gui(...)
pane = excelui.attach_pane('MyPane', pane=OurWxPane)
# The frame is in the pane's `frame` attribute
pane.frame.set_progress(3)
We ensure `set_progress` is run on the *wx* thread, by decorating it with :any:`xloil.gui.wx.wx_thread`.
wxPython Thread-safety
______________________
The :any:`xloil.gui.wx.wx_thread` function behaves the same as `Qt_thread` described
in :ref:`xlOil_Python/CustomGUI:Qt Thread-safety`.
Task Pane Events
================
Custom task panes have three events which can be handled by defining methods in the subclass of
:any:`xloil.gui.CustomTaskPane` used to create the pane. The callbacks occur on Excel's main thread.
The events are:
::
def on_docked(self):
# Called when the user docks or undocks the pane. The dock position is in 'self.position'
...
def on_visible(self, state: bool):
# Called when the user closes/shows the pane with the new visibility in 'state'
...
def on_destroy(self):
# Called just before the pane is destroyed when the parent window is closed
super().on_destroy() # Important!
...
Task Pane registry
==================
The created task panes are automatically stored in a registry so there is no need to hold a
reference to them. Task panes are attached by default to the active window and it is possible to
have multiple windows per open workbook. xlOil will free the panes when the parent workbook ora
addin closes.
We can search the registry by name for a task pane without having the :obj:`xloil.ExcelGUI` object:
::
pane = xloil.gui.find_task_pane("MyPane")
By default, xlOil looks for a pane attached to the active window, but this can be changed with
arguments. It is possible to create multiple panes with the same name, in which case this search
could return either one.
Async GUI Calls
---------------
The above examples create the GUI calls in a synchronous fashion but many of the GUI functions
are or can be async. Because xlOil loads modules in a background thread, it's not necessary
to do this to keep Excel responsive but it could be useful in some circumstances.
::
async def make_gui():
# With connect=False the ctor does nothing
excelui = xlo.ExcelGUI(xml=..., funcmap=..., connect=False)
# The action happens when we call connect, which returns a awaitable future
await excelui.connect()
# We can also create the pane async by passing an awaitable but we have
# to then pass the name explictly
await excelui.attach_pane_async(
name='TkPane',
pane=Tk_thread().submit_async(TkTaskPane))
# We need to keep a reference to 'excelui' as deleting it disconnects the UI
return excelui, pane
================================================
FILE: docs/source/xlOil_Python/Debugging.rst
================================================
==============================
xlOil Python Debugging
==============================
Only python debuggers capable of handling *embedded* python interpreters can be used to debug xlOil
python code.
Visual Studio
-------------
*Visual Studio 2019* and *Visual Studio 2022* with Python Tools installed can break into xlOil python code.
Attach to the relevant Excel process selecting *only Python Code* debugging.
Visual Studio will occasionally hang when first hitting a python breakpoint. In the case, restart VS and
Excel and re-try
.. note::
As of Aug 2022, mixed mode debugging with both Python and Native C code does not work: Python
breakpoints are not hit.
VS Code
-------
*VS Code* can break into xlOil python code. You need to tell xlOil to start a `debugpy` server by
selecting it as the debugger in the xlOil ribbon or the ini file. Then use VS Code's python
remote debugger to connect. The default port is *5678* but this can be changed in the ini file.
*VS Code* cannot hit breakpoints in async and RTD functions even though tracing is enabled in the
relevant threads. The reason for this is unknown.
.. note::
Running the *debugpy* server has some peformance implications, so avoid leaving debugging
enabled when not required.
Pdb
---
Pdb can be used for post-mortem debugging, i.e. after an exception occurs. Select *pdb* in as
the debugger in the xlOil ribbon or the ini file. Use the command `breakpoint()` in your
code to trigger the debugger, or wait for an exception.
Selecting the debugger programmatically
---------------------------------------
A debugger can be selected at runtime in the xlOil ribbon but this choice will persist when Excel
is restarted. Alternatively, the debugger can be choosen in code, which is not persistent. Use
the following python code:
::
import xloil.debug
xloil.debug.use_debugger('pdb')
Or the Excel formula:
::
=xloPyDebug(...)
================================================
FILE: docs/source/xlOil_Python/DistributingAddins.rst
================================================
=======================================
xlOil Python Distribution and Packaging
=======================================
xlOil supports two possiblilities for distribution:
* Creating an XLL addin to distribute code to existing xlOil users
* Packaging Python and xlOil to install code for new users
Creating an Addin
=================
The idea of this approach is to create an XLL addin so users don't have to edit
their `xlOil.ini` to load some packaged code.
Users of XLL will need access to a python distribution which contains
the *xlOil* package, however they do not need the xlOil addin installed.
At a command prompt, run:
::
xloil create myaddin.xll
This will create a `myaddin.xll` and `myaddin.ini` in the current directory.
By default, the XLL will try to load `myaddin.py` in the same directory, so
create this file:
::
import xloil as xlo
@xlo.func
def MySum(x, y, z):
'''Adds up numbers'''
return x + y + z
Dropping `myaddin.xll` into an Excel session will create the function ``MySum``.
If the python distribution will be in a standard directory on users' machines,
the `PYTHONEXECUTABLE` and `PATH` environment variables in `myaddin.ini` can
be used to point to it, otherwise these variables can read the python location
from the registry.
If you copy `myaddin.xll`, `myaddin.ini` and `myaddin.py` to your `%APPDATA%\\Microsoft\\Excel\\XLSTART`
directory, Excel attempts to opens the ini file and py files which is not ideal! xlOil will
look for `myaddin.ini` in `%APPDATA%\\xlOil`, so install the ini to there instead. You'll also need
to choose a directory to hold `myaddin.py` (or other python modules) and ensure `myaddin.ini` points to
it; `%APPDATA%\\xlOil\\myaddin` could be a sensible choice.
.. important::
If a user of `myaddin.xll` has an ini file at `%APPDATA%\\xlOil\\xlOil.ini``
the core xlOil.dll is loaded using those settings before `myaddin.xll`.
The assumption is that the user has the xlOil addin installed in Excel, but
since only one instance of xlOil (and one python interpreter) can be hosted in
Excel, one settings file must take precedence. You can make your addin take
precedence with the `LoadBeforeCore` flag in `myaddin.ini`.
Packaging Python
================
xlOil can use `PyInstaller `_ to package a python distribution and
create an installer executable. Support for this is fairly rudimentary at present.
You shouuld start with a minimum python distribution (ideally based on the standard distribution)
for the code you want to distribute, otherwise *PyInstaller* may create a very large output. The
distribution should include xlOil and be able to load your code in Excel.
To package the settings in the file *myaddin.ini* which loads the python module *excel_funcs.py*,
run the following command:
::
xloil package myaddin.ini --hidden-import excel_funcs
Note that *--hidden-import* is actually an `argument to PyInstaller `_
and can be specified multiple times. Any other trailing arguments will be passed directly to *PyInstaller*.
The resulting *dist* directory will contain:
* install_main.exe (installs xlOil)
* _internal (contains the python distribution)
The installer does not copy the python distribution, it is used in-situ
Customising the packaging
-------------------------
Calling
::
xloil package -makespec myaddin.ini
stops the packaging process before after creation of the
`PyInstaller spec files `_. You can edit this
spec file directly as described in the *PyInstaller* docs, then invoke *PyInstaller* on the spec file
yourself to finish the process.
================================================
FILE: docs/source/xlOil_Python/Dynamic.rst
================================================
========================================
xlOil Python Dynamic Runtime Interaction
========================================
xlOil function registration can be controlled dynamically at runtime. Also,
the Excel can be controlled from macros or the console.
.. contents::
:local:
Dynamic Import
--------------
Functions for registration can be specified at runtime without the need to
add an :any:`xloil.func` decorator.
.. note::
Although Excel will let you, avoid doing this from (non-async) worksheet functions
since creating new functions *during* Excel's calculation cycle is likely to cause
instability.
The :any:`xloil.import_functions` call provides an analogue of ``from X import Y as Z``
with Excel UDFs. A simple usage is:
::
xloil.import_functions("c:/lib/AnotherFile.py", names=["greet"], as_names=["PyGreet"])
where AnotherFile.py contains:
::
def greet(x:str):
return f"Hello {x}!"
We specify the Excel name of the function explicitly, if we omitted this, the function
would be registered with its python name. In Excel you can then use the formula
``=greet("World")``.
Typing annotations are respected, as are doc-strings - the import behaves as if we had
decorated the function with :any:`xloil.func`.
In a worksheet, :any:`xloil.import_functions` is exposed as ``xloImport`` with the same
arguments.
Since the import machinery can register *any* callable, including class constructors,
you cane be a little creative. For example, the following cell formulae will
create a *pandas* *DataFrame* from the range `C1:F5`, sum over rows and take the average
of the result.
::
[A1] : =xloImport("pandas","DataFrame")
[A2] : =DataFrame(C1:F5)
[A3] : =xloAttr(xloAttrObj(A2,"sum",{"axis",1}), "mean")
Notice we used ``xloAttrObj`` - the output of this is always a cache reference. This stops
xlOil from trying to convert the result to an Excel value. Since a *DataFrame* is iterable
it would otherwise output *DataFrame.index* as an array. Also note the convenient use of
`array constants `_
to specify keyword arguments.
Dynamic Registration
--------------------
Functions can be registed using :any:`xloil.register_functions` and deregistered
with :any:`xloil.deregister_functions`.
For example:
::
def Greet(x):
return f"Hello {x}"
xlo.register_functions([Greet])
Any callable can be registered, for example:
::
class Closure:
self._total = 0
def __call__(self, x):
self._total += x
return x
xlo.register_functions(Closure())
The name and help of the function can be controlled using :any:`xloil.func`
and the function can be linked to a specific python module, which means
it will be removed if the module is unloaded or reloaded.
::
xlo.register_functions(
[xlo.func(fn=Closure(), name=f"Dynamic1", register=False)],
module=sys.modules[__name__])
Functions are deregistered by name:
::
xlo.deregister_functions("Greet")
================================================
FILE: docs/source/xlOil_Python/Example.rst
================================================
======================
xlOil Python Examples
======================
This is code for the xlOil python test spreadsheet. You can find the associated sheet at
:ref:`core-example-sheets`.
.. literalinclude:: /../../tests/AutoSheets/PythonTest.py
================================================
FILE: docs/source/xlOil_Python/ExampleGUI.rst
================================================
=========================
xlOil Python GUI Examples
=========================
This is code for the xlOil python test spreadsheet. You can find the associated sheet at
:ref:`core-example-sheets`.
.. literalinclude:: /../../tests/ManualSheets/python/PythonTestUI.py
================================================
FILE: docs/source/xlOil_Python/ExampleRTD.rst
================================================
=========================
xlOil Python RTD Examples
=========================
This is code for the xlOil python test spreadsheet. You can find the associated sheet at
:ref:`core-example-sheets`.
.. literalinclude:: /../../tests/ManualSheets/python/PythonTestAsync.py
================================================
FILE: docs/source/xlOil_Python/ExcelApplication.rst
================================================
==========================================
xlOil Python: The Excel.Application object
==========================================
.. contents::
:local:
Introduction
------------
The `Excel.Application` object is the root of Excel's COM interface. If you have used VBA you
will likely have come across it. If xlOil is running embedded in Excel, you can get a reference
to the parent application with :any:`xloil.app`. If xlOil has been imported as package, you can
create an Application with :any:`xloil.Application`.
xlOil mirrors a small part of the `Excel.Application` object model as discussed below. For other calls,
the COM interface can be accessed directly which provides syntax similar to the `Application`` object in
VBA.
.. important::
All COM calls must be invoked on the main thread! A function runs in the main thread if is
not declared multi-threaded or if it is called from VBA or the GUI. However during Excel's calc
cycle, much of the Application object model is locked, in particular, writing to the sheet is blocked.
To schedule a callback to be run on main thread use :any:`xloil.excel_callback`.
The object model is documented extensively at `Excel object model overview `_
and `Application Object `_
Calling Worksheet Functions and Application.Run
-----------------------------------------------
In VBA, ``Application.Run`` takes a function name and a variable argument list and attempts
to call the specified user-defined function. In xlOil, use :obj:`xloil.run` to make the same
call or go via the COM library with ``xloil.app().Run(...)``. All COM calls must be invoked
on the main thread, however :obj:`xloil.run` and :obj:`xloil.call` have async flavours
:obj:`xloil.run_async` and :obj:`xloil.call_async` which return a Future and can be called
from any thread.
To call a worksheet function, use :obj:`xloil.call`. This can also invoke old-style
`macro sheet commands `_.
It must be called from **a non-local worksheet function**. Worksheet functions can be
called from COM, for example, ``xloil.app().WorksheetFunction.Sum(...)``.
+-------------------------------+---------------------------------------------------------+------------------------------+
| Function | Use | Call from |
+===============================+=========================================================+==============================+
| :obj:`xloil.run` | Calls user-defined functions as per `Application.Run` | Main thread |
+-------------------------------+---------------------------------------------------------+------------------------------+
| :obj:`xloil.run_async` | (as above but async) | Anywhere |
+-------------------------------+---------------------------------------------------------+------------------------------+
| :obj:`xloil.call` | Calls worksheet functions, UDFs or macro sheet commands | Non-local worksheet function |
+-------------------------------+---------------------------------------------------------+------------------------------+
| :obj:`xloil.run_async` | (as above but async) | Anywhere |
+-------------------------------+---------------------------------------------------------+------------------------------+
| xloil.app().WorksheetFunction | Calls worksheet functions | Main thread |
+-------------------------------+---------------------------------------------------------+------------------------------+
xlOil's Excel Object Model
--------------------------
xlOil mirrors a small part of the `Excel.Application` object model to faciliate easier access to the commonly
used :obj:`xloil.Application`, :obj:`xloil.Workbook`, :obj:`xloil.Worksheet`, :obj:`xloil.ExcelWindow`, and
:obj:`xloil.Range` objects.
Each of xlOil's application objects provides a `to_com` method which accepts an optional *lib* argument.
Calling this returns a marshalled COM object which supports any method or property in the full Application object
model. COM support is be provided by `comtypes `_ , a newer pure
python package or `win32com `_
a well-established C++ based library. If omitted, the default is 'win32com'. The default can be changed
in the XLL's ini file.
COM methods can be called directly on xlOil's application objects, so the following are equivalent:
::
xlo.Application().RegisterXLL(...)
xlo.Application().to_com().RegisterXLL(...)
There is no ambiguity with other methods on the *Application* object as COM methods and properties
all start with a capital letter.
COM methods can be called with keyword arguments - note COM arguments start with a capital letter.
::
xloil.app().Selection.PasteSpecial(Paste=xloil.constants.xlPasteFormulas)
Excel Automation
----------------
Excel's COM interface allows the application to be driven externally by a script. This is best explored
by looking at (a simplified version of) xlOil's test runner. The test runner is started at the command line,
rather than inside an Excel instance like an xlOil-based addin. You may want to look at the documentation
for Excel's `Name `_ object.
::
import xloil as xlo
# Create a new Excel instance and make it visible
app = xlo.Application()
app.visible = True
# Load addin
if not app.RegisterXLL("xloil.xll"):
raise Exception("xloil load failed")
test_results = {}
for filename in ['TestUtils.xlsx, PythonTest.xlsm']:
# Open the workbook in readonly mode: don't change the test source!
wb = app.open(filename, read_only=True)
app.calculate(full=True)
# Loop through all named ranges in the workbook, looking for ones
# prefixed with 'Test_'. We expect those ranges to contain True
# for a successful test outcome.
names = wb.to_com().Names
for named_range in names:
if named_range.Name.lower().startswith("Test_"):
# skip one char as RefersTo always starts with '='
address = named_range.RefersTo[1:]
test_results[(filename, named_range.Name)] = wb[address].value
wb.close(save=False)
app.quit()
if not all(test_results.values()):
print("-->FAILED<--")
Creating an Application
=======================
The :any:`xloil.Application` object can be created in several ways:
1) When xloil is embedded, the parent applicaton object is in :any:`xloil.app()`
2) `xlo.Application()` with no arguments opens an new instance of Excel (but does not make it visible)
3) `xlo.Application("MyWorkbook.xlsx")` returns an instance of Excel which has *MyWorkbook.xlsx* open (or throws)
4) `xlo.Application(ComObject)` points an Application at a COM object managed by *win32com* or *comtypes*
5) `xlo.Application(HWND)` creates a Application given the window handle of Excel's main window as an int
The application object can be :any:`xloil.Application.quit()` manually or since it is a context manager,
you can write:
::
with xloil.Application() as app:
# do stuff
...
# app has been quit without saving any open Workbooks
Accessing Sheets and Ranges
---------------------------
When xlOil is embedded in Excel as an addin, there is a natural default :obj:`xloil.Application`
object: the parent application, which can be accessed by :any:`xloil.app()`. Additionally,
when embedded you can unambigiously create :any:`xloil.Range` and :any:`xloil.Worksheet` objects
without needing to specify the application.
Reading from a Range
====================
::
import xloil as xlo
# if xlOil is embedded: no need to specify Application.
# Returns a numpy array
xlo.Range("A1:C1").value
# Above is equivalent to
xlo.app().range("A1:C1").value
# Using COM (win32com) to access a range with empty index
# Returns a tuple rather than a numpy array
xlo.app().Range("A1", "C1").Value
If the range referred to is empty, its `value` array will be populated with `None`. This
is different to array/range arguments to :any:`xloil.func` worksheet functions where the
array is trimmed to the last non-blank. This behaviour can be replicated with
:any:`xloil.Range.trim` :
::
r = xlo.app().range("A1:C1")
r.clear()
r.trim().value # returns the array [None]
r.set(1)
r.trim().value # returns the array [1, 1, 1]
The square bracket (getitem) operator for a Range behaves like a numpy array,
in that if the tuple specifies a single cell, it returns the value in that cell, otherwise
it returns a :any:`xloil.Range` object. To create a range consisting of a single cell
use the `cells` method.
Writing to a range
==================
::
# Using the COM object
xlo.app().Range("A1", "B2").Value = ((1, 2), (3, 4))
rng = xlo.Range("A1:B2")
# Using xlOil syntax (can use numpy array)
rng.value = np.array([[1, 2], [3, 4]])
# Set the entire range to a single value
rng.set("hello")
# Add something
rng += " world!"
Writing a cell formula
======================
Use the :any:`xloil.Range.formula` property to set or retrieve the formula in a cell
::
xlo.app().Range("A1").formula = '=SUM(B1:B10)'
xlo.log(xlo.app().Range("A1").has_formula) # Will write True
When write addresses used in formula strings, :any:`xloil.Address` can be useful e.g.:
::
sum_start_row=0
sum_end_row=9
address = xlo.Address((sum_start_row, 1, sum_end_row, 1), sheet="Sheet1")
xlo.app().Range("A1").formula = f'=SUM({address})' # Will be '=SUM(Sheet1!B1:B10)'
Since the introduction of dynamic arrays, array formulae have become largely obselete, but it
is possible to set them with :any:`xloil.Range.set_formula`.
Using Worksheets and Workbooks
==============================
There are several ways to address or refer to part of a worksheet:
::
wb = xloil.active_workbook() # Only available when embedded
# Specify external Excel range address
r1 = xlo.app().range[f'{wb.name}Sheet1!B2:D3']
# Specify workbook Excel range address
r1 = wb['Sheet1!B2:D3']
# Specify worksheet, then local Excel range address
ws = wb['Sheet1']
r1 = ws['B2:D3']
# The range function, like in Excel includes right and left hand ends
r2 = ws.range(from_row=1, from_col=1, to_row=2, to_col=3)
# The slice syntax follows python conventions so only the left
# hand end is included
r3 = ws[1:3, 1:4]
The square bracket (getitem) operator for :any:`xloil.Worksheet` always returns
a :any:`xloil.Range`. For :any:`xloil.Workbook` it may return a :any:`xloil.Range`
or a :any:`xloil.Worksheet`.
Writing to a worksheet
~~~~~~~~~~~~~~~~~~~~~~
::
data = np.array([[1, 2], [3, 4]])
ws = xloil.worksheets['Sheet1']
# ws[...] gives a Range, so
ws["A1:B2"].value = data
# However, value is optional when writing to a sheet
ws["A1:B2"] = data
# You can copy another part of the sheet, it's faster to
# drop the value property here
ws["A1:B2"] = ws["D1:E2"]
# Also works for Workbooks
wb = xloil.active_workbook()
wb['Sheet1!B2:D3'] = ws["D1:E2"]
Pausing Excel Calculations
--------------------------
When writing to worksheets, performance can often be improved by disabling Excel's auto calculation
and Event model, otherwise calculation cycles and events will be triggered on each write.
This is straightforward using :any:`xloil.PauseExcel`:
::
with xloil.PauseExcel() as paused:
for i in range(100):
worksheet[i, 1].value = i
The context manager can be replicated manually with
::
try:
xloil.app().ScreenUpdating = False
xloil.app().EnableEvents = False
xloil.app().Calculation = xloil.constants.xlCalculationManual
...
finally:
xloil.app().ScreenUpdating = True
xloil.app().EnableEvents = True
xloil.app().Calculation = xloil.constants.xlCalculationAutomatic
Troubleshooting
---------------
Both *comtypes* and *win32com* have caches for the python code backing the Excel object model. If
these caches somehow become corrupted, it can result in strange COM errors. It is safe to delete
these caches and let the library regenerate them. The caches are located at:
* *comtypes*: `/site-packages/comtypes/gen`
* *win32com*: run ``import win32com; print(win32com.__gen_path__)``
See `for example `_
Note: as of 25-Jan-2022, *comtypes* has been observed to give the wrong answer for a call to
`xloil.app().Workbooks(...)` so it is no longer used as the default whilst this is investigated.
================================================
FILE: docs/source/xlOil_Python/ExternalPackages.rst
================================================
=============================
xlOil Python Package Support
=============================
xlOil has built-in converters for several external libraries
.. contents::
:local:
Pandas
------
Pandas support can be enabled in *xlOil* with:
::
import xloil.pandas
This registers a return converter which allows pandas dataframes to be returned
from Excel functions in a natural table format. Not if a function always returns
a dataframe it is more performant to specify ``pandas.DataFrame`` as the return type
annotation.
The annotation ``pandas.DataFrame`` can also be used for arguments. The converter by
default expects a two dimensional array with headings in the first row.
To gain more control over the *DataFrame* conversion, use the annotion
`xloil.pandas.PDFrame`. Examples:
::
@xlo.func
def getDataField(data: PDFrame(headings=True), columns) -> PDFrame(headings=False):
return data[columnName]
@xlo.func
def readDatedData(df: PDFrame(headings=True, index=['Date', 'Type'], dates=['Date'])):
# df will have a pandas.MultiIndex index
return df
The parameters have slightly different interpretations when reading or outputting arguments.
The `headings`` parameter when reading, if True, interprets the first row as column heading or
if it is an int inperets the first *N* rows as a *pandas.MultiIndex* heading. When outputting,
if it is True/False outputs column headings or not.
The `index`` parameter when reading specifies column(s) which should be set as the index: xloil
calls `DataFrame.set_index()`, so a column name or list-like of columns names can be
given. When writing: if *index* is set to False, the index is not output, otherwise it is.
The `dates` parameters specifies which columns to convert from from Excel serial date numbers
to *numpy.datetime64*. Since Excel stores dates as floating points it is not possible for
xlOil to identify date columns automatically. This parameter has no effect when outputting as
*datetime* arrays are converted automatically.
We can call the *PDFrame* converter in the function itself if we want to control the arguments
passed to it based on the function's inputs (this isn't possible if it is used as a decorator).
::
@xlo.func
def dFrameMultiHeadings(
df: PDFrame(headings=2),
outputHeadings=False):
return PDFrame(headings=outputHeadings)(df)
See :doc:`Example` for more examples.
Matplotlib
----------
Importing ``xloil.matplotlib`` defines a return converter so matplotlib figures
can be returned from worksheet functions. By default they are resized to the cell.
Subseqent figures returned from the same cell overwrite previous ones.
Returning a figure requires setting ``macro=True`` in the :obj:`xloil.func` declaration.
::
import xloil.matplotlib
from matplotlib import pyplot
@xlo.func(macro=True)
def pyTestPlot(x, y, **kwargs):
fig = pyplot.figure()
ax.plot(x, y, **kwargs)
return fig
@xlo.func(macro=True)
def pyTestPlot(x, y, width, height, **kwargs):
fig = pyplot.figure()
ax.plot(x, y, **kwargs)
return xloil.matplotlib.ReturnFigure(size=(width, height), pos='top')(fig)
PIL / pillow
------------
Importing ``xloil.pillow`` defines a return converter so PIL images
can be returned from worksheet functions. By default they are resized to the cell.
Subseqent images returned from the same cell overwrite previous ones.
Returning an image requires setting ``macro=True`` in the :obj:`xloil.func` declaration.
::
import xloil.pillow
from PIL import Image
@xlo.func(macro=True)
def pyTestPic():
im = Image.open("MyPic.jpg")
return im
================================================
FILE: docs/source/xlOil_Python/FAQ.rst
================================================
======================
xlOil Python Questions
======================
.. contents::
:local:
ImportError: Typelib different than module
------------------------------------------
When using `comtypes` for COM support, then auto-generated modules can go out of sync, for example, if
you upgrade `comtypes`. Fix this at a command prompt with
.. highlight:: dosbatch
::
C:\ > where clear_comtypes_cache.py
C:\MyPythonDist\Scripts\clear_comtypes_cache.py
C:\ > python C:\MyPythonDist\Scripts\clear_comtypes_cache.py -y
You'll probably need to restart Excel.
Intellisense / Function Context Help
------------------------------------
To activate pop-up function help, follow the instructions here: :any:`concepts-intellisense`.
Auto-reload doesn't reload a module
-----------------------------------
xlOil only watches for changes in modules which contain :any:`xloil.func` regisrations: it does
not do a deep scan / reload of all dependent modules as this could include large portions of your
python distribution!
Dynamically Resized Arrays
--------------------------
This is available in Office 365. It would be possible to replicate this behaviour in older Excel
versions however it is somewhat tricky, as the output ranges are not 'protected' as they are with
array formulae or with the Office 365 support. The code would need to:
1. Hook the *AfterCalculate* event.
2. Remember which functions output arrays in the current calc cycle and their calling cell.
3. Remember which functions output arrays in the previous calc cycle and their calling cell.
4. On *AfterCalculate*, loop through the functions in (2) writing their array to the worksheet.
5. When writing the array, take care to clear any previous result from (3) but not to overwrite
any other non-empty cells.
6. Clear the ranges for functions in (3) but not in (2).
7. Carefully handle the case where the output range for a function in (3) has been edited, for example
it may now contain a function in (2)!
Unlike the built-in support in Office 365, the written arrays would be static data so, for example,
function dependency tracing would not work on them (except the top left entry).
This application failed to start because it could not find or load the Qt platform plugin "windows"
---------------------------------------------------------------------------------------------------
Under Anaconda (and possibly other distributions), Qt is installed outside the usual site-packages
location. A *qt.conf* file in the root dir directs Qt to the correct place. However under Windows,
Qt will only look for this file in the directory returned by GetModuleFileName or in :/qt/etc/ which
is unlikely to exist or be easily creatable on Windows. (See https://doc.qt.io/qt-6/qt-conf.html)
In our case GetModuleFileName always returns Excel.exe.
xlOil attempts to work around this by passing the correct location to Qt on start up, however for
non-standard locations like virtual environments it may be necessary to set the
`QT_QPA_PLATFORM_PLUGIN_PATH` environment variable explicitly in xlOil.ini. If `QT_QPA_PLATFORM_PLUGIN_PATH`
is set, xlOil will not try to workaround this issue, so verify the value of this variable in the
lauching environment.
win32com / pythoncom: has no attribute 'CLSIDToClassMap'
----------------------------------------------------------------------
The cache used by *win32com* has somehow gone wrong. Deleting the cache dir will cause a rebuild
which usually resolves the issue. The location of this directory can be discovered with
::
python -c "import win32com; print(win32com.__gen_path__)"
Either delete the entire *gen_py* directory or just the subdirectory corresponding to the CLSID
which is indicated in the error.
Could not connect COM: trying again in 1 second
-----------------------------------------------
If this appears frequently in your log file, it may be because Excel is not opening a blank
workbook on load. It is a long-standing Excel bug that the COM server is not properly
started until a workbook is opened: xlOil needs this COM server to register ribbon components,
so waits in a loop until is is available. To make Excel open a blank workbook on load (the
pre-Office 2016 behaviour), go to the *File* menu, then *Options*, under *General*, find
*Start up options*, uncheck the "Show the Start screen when this application starts" box.
================================================
FILE: docs/source/xlOil_Python/Functions.rst
================================================
==================================
xlOil Python Registering Functions
==================================
.. contents::
:local:
There are several ways a python function can be registered with Excel via arguments to
:any:`xloil.func` decorator.
::
@xloil.func
def Greeting(who):
return "Hello " + who
Local Functions
---------------
When registering functions from :ref:`xlOil_Python/Concepts:Workbook Modules`, xlOil defaults to making
any declared functions "local": this means their scope is limited to the workbook.
It also means the function is automatically macro-type (xlOil achieves this by creating
a VBA stub to invoke them).
This behaviour can be overriden by `local` argument:
::
@xloil.func(local=False)
def Greeting(who):
return "Hello " + who
Local functions have some limitations compared to global scope ones:
- No native async or threadsafe, but RTD async is OK
- Slower due to the VBA redirect
- Associated workbook must be saved as macro-enabled (xlsm extension)
- No function wizard help, but CTRL+SHIFT+A to show argument names is available
(Technical note: It is possible to use the Application.MacroOptions call to add help to the
function wizard for VBA, but identically named functions will conflict which rather defeats
the purpose of local functions).
Async and RTD Functions
-----------------------
RTD (real time data) functions are able to return values independently of Excel's
calculation cycle and correspond to `async generators `_
in python. For example, the function below returns the time every two seconds:
::
import xloil, datetime, asyncio
@xloil.func
async def pyClock():
while True:
await asyncio.sleep(2)
yield datetime.datetime.now()
This is discussed in detail in :ref:`xlOil_Python/Rtd:Introduction`.
Commands, Macros & Subroutines
------------------------------
'Macros' in VBA are declared as subroutines (``Sub``/``End Sub``) and do not return a value.
These functions run outside the calculation cycle, triggered by some user interaction such
as a button. They run on Excel's main thread and have full permissions on the Excel object
model. In the XLL interface, these are called 'commands' in the XLL interface and xlOil uses
this terminology.
Programs which heavily use the :ref:`xlOil_Python/ExcelApplication:Introduction` object model are usually written as
commands.
::
@xloil.func(command=True)
def pressRunTests():
r = xloil.Range("TestArea")
r.clear()
r.set("Foo")
...
If not :ref:`xlOil_Python/Functions:Local Functions`, XLL commands are hidden and not displayed in
dialog boxes for running macros, such as Excel's macro viewer (Alt+F8). However their
names can be entered anywhere a valid command name is required, including in the macro
viewer.
Multi-threaded functions
------------------------
Declaring a function re-entrant tells Excel it can be called on all of its calculation
threads simultaneously - any other function is invoked on the main thread.
:ref:`xlOil_Python/Functions:Local Functions` cannot be declared re-entrant.
Since python (at least CPython) is single-threaded there is no direct performance
benefit from enabling this. However, if you make frequent calls to C-based libraries
speed gains may be possible.
::
import xloil, ctypes
@xloil.func(local=False, threaded=True)
def threadsafe(x: float) -> int:
# Do lots of calculations
...
# Return the thread ID to prove the functions were executed on different threads
return ctypes.windll.kernel32.GetCurrentThreadId(None)
Dynamic Registration
--------------------
Functions for registration can be specified at runtime without the need to decorate them
with :any:`xloil.func`.
.. note::
Although Excel will let you, avoid doing this from (non-async) worksheet functions
since creating new functions *during* Excel's calculation cycle is likely to cause
instability.
The :any:`xloil.import_functions` call provides an analogue of ``from X import Y as Z``
with Excel UDFs. A simple usage is:
::
xloil.import_functions("c:/lib/AnotherFile.py", names=["greet"], as_names=["PyGreet"])
where AnotherFile.py contains:
::
def greet(x:str):
return f"Hello {x}!"
We specify the Excel name of the function explicitly, if we omitted this, the function
would be registered with its python name. In Excel you can then use the formula
``=greet("World")``.
Typing annotations are respected, as are doc-strings - the import behaves as if we had
decorated the function with :any:`xloil.func`.
In a worksheet, :any:`xloil.import_functions` is exposed as ``xloImport`` with the same
arguments.
Since the import machinery can register *any* callable, including class constructors,
you cane be a little creative. For example, the following cell formulae will
create a *pandas* *DataFrame* from the range `C1:F5`, sum over rows and take the average
of the result.
::
[A1] : =xloImport("pandas","DataFrame")
[A2] : =DataFrame(C1:F5)
[A3] : =xloAttr(xloAttrObj(A2,"sum",{"axis",1}), "mean")
Notice we used ``xloAttrObj`` - the output of this is always a cache reference. This stops
xlOil from trying to convert the result to an Excel value. Since a *DataFrame* is iterable
it would otherwise output *DataFrame.index* as an array. Also note the convenient use of
`array constants `_
to specify keyword arguments.
More controlled registration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
:any:`xloil.register_functions`
::
class Closure:
self._total = 0
def __call__(self, x):
self._total += x
return x
funcs.append(
xlo.func(fn=Closure(), name=f"dynamic1", register=False)
)
xlo.register_functions(funcs, sys.modules[__name__])
Loads functions from the specified source and registers them in Excel. The functions
do not have to be decorated, but are imported as if they were decorated with ``xloil.func``.
So if the functions have typing annotations, they are respected where possible.
This function is intended
Imports the specifed python module and registers any it for xloil
functions it contains. Leaving the argument blank loads or reloads the
workbook module for the calling sheet, i.e. the file `WorkbookName.py`.
================================================
FILE: docs/source/xlOil_Python/GettingStarted.rst
================================================
=========================
xlOil Python Introduction
=========================
.. contents::
:local:
Introduction
------------
The Python plugin for xlOil primarily allows creation of Excel functions and macros
backed by Python code. In addition it offers full control over GUI objects and an
interface for Excel automation: driving the application in code.
When loaded by xlOil, *xlOil_Python* loads specified python modules, looking for functions
to add to Excel's global name scope, like an Excel addin. The plugin can also look for modules
of the form .py and load these too, this is like creating a VBA code module for
a workbook. Any python module which contains Excel functions is watched for file modifications so
code changes are reflected immediately in Excel.
*xlOil_Python* is tightly integrated with numpy, allowing creation of fast Excel array
functions.
*xlOil_Python* can be imported in python code to allow remote control of an Excel application.
See :doc:`ExcelApplication`
For examples of worksheet functions and GUI controls have a look at :doc:`Example` and
:ref:`core-example-sheets`.
Getting Started
---------------
**You must use the same bit-ness of python and Excel**. So if your Excel is 32-bit, you must
install xloil using a 32-bit python distibution.
Run the following at a command prompt with python environment settings:
::
pip install xlOil
xlOil can now be imported to allow remote control of an Excel application. See :doc:`ExcelApplication`
To install the addin which allows you to create python-based Excel functions, type:
::
xloil install
This call registers the xlOil addin with Excel and places a settings file at
`%APPDATA%/xlOil/xlOil.ini`. The settings file describes the python modules
which will be loaded and sets the paths to the python distribution. xlOil should
set the python paths automatically, but they can be overriden if required.
To test the setup, you can try the python example sheet: :ref:`core-example-sheets`.
.. note::
It's not necessary for ``xlOil.xll`` to be registered in this way: you can just
drop it into your Excel session when required.
You now have three ways to get xlOil to load your python code.
My first xlOil module
~~~~~~~~~~~~~~~~~~~~~
Create a `MyTest.py` file with the following lines:
::
import xloil as xlo
@xlo.func
def Greeting(who):
return "Hello " + who
Open Excel and use the *xlOil* ribbon toolbar to ensure the search paths include
the directory containing `MyTest.py`. Then add 'MyTest' to the *Load Modules*
field, separating entries with a comma. The order of these steps matters because
editing the *Load Modules* field triggers a load of all newly added modules.
Call the `=Greeting("world")` function in a cell.
My first workbook module
~~~~~~~~~~~~~~~~~~~~~~~~
Create an Excel workbook called `MyBook`. In the same directory, create a
file `MyBook.py` containing the following:
::
import xloil as xlo
@xlo.func
def Adder(x, y):
return x + y
You need to open and close `MyBook` in Excel for xlOil to find the python file.
Now try invoking the `=Adder()` function - it can also add arrays!
If this isn't working, ensure that "Trust access to the VBA object model" is
checked in *Excel Options -> Trust Centre -> Macro Settings* - this setting
is off by default.
Using the Ribbon
----------------
xlOil's Ribbon toolbar can:
* Change the python environment (reqires restart)
* Select modules to load at startup and *sys.path* to set
* Open the log file
* Open a console to interact with the embedded python environment
* Choose a debugger, see :ref:'xlOil_Python/Debugging'
* Select date formats to use when parsing strings
The toolbar edits the settings file so that changes persist. The ribbon is enabled by
but can be disabled by removing it from the specified *Load Modules*.
.. note::
If you have an old ini file (prior to v0.15), you will need to upgrade it to use the
ribbon toolbar. Remove the old ini file and remove/install xlOil.
Troubleshooting
---------------
If xlOil detects a serious load error, it pops up a log window to alert you (this can
be turned off). If it succesfully loaded the core DLL a log file will also be created
in `%APPDATA%/xlOil` next to `xlOil.ini`. The worksheet function `=xloLog()` will tell
you where this file is.
Normally a python distribution or environment can be loaded with only the location of
*python.exe* passed via the `PYTHONEXECUTABLE` environment varaible. For more complex
setups, you may need to set the python paths, i.e. `PATH` and `PYTHONPATH` and maybe even
`PYTHONHOME`, in the `xlOil.ini` file for xlOil to load your python distribution.
If the xlOil ribbon does not appear, check that `xloil.xloil_ribbon` appears in the
*LoadModules* key in the ini file.
Intellisense / Function Context Help
------------------------------------
To activate pop-up function help, follow the instructions here: :any:`concepts-intellisense`.
================================================
FILE: docs/source/xlOil_Python/Jupyter.rst
================================================
=========================
xlOil Jupyter Interaction
=========================
.. contents::
:local:
Introduction
------------
xlOil can connect to a Jupyter python kernel, allowing interaction between a Jupyter notebook
and Excel. To use this functionality, either run `=xloPyLoad("xloil.jupyter")` in a cell or
load the `xloil.jupyter` module by specifying it in the `xlOil.ini` file:
::
...
[xlOil_Python]
LoadModules=["xloil.jupyter"]
To establish the connection, call the `xloJpyConnect` function. You pass it one of the following:
1. The name of a notebook e.g. `MyBook.ipynb`. In this case all local jupyter instances
will be searched to find which one has this notebook open.
2. The full URL to the notebook, e.g.
`http://localhost:8888/notebooks/MyBook.ipynb?token=ac3894ab667fa1f3e4f7fe473fa89566a1580cdb49a2649b`
3. The `kernel-xxxx-xxx-xxx.json` file which is specified in the output of running
the magic `%connect_info` in a Jupyter cell (no file path is required).
The `xloJpyConnect` function will return a cache reference.
.. note::
The targeted jupyter kernel can be running any version of Python 3 and does not need to
to have the `xloil` package installed.
Registering an Excel function from Jupyter
------------------------------------------
After the connection is estabilished, any function created in the kernel and decorated with
`@xloil.func` will be registed in Excel just as if it had been loaded by xlOil in the normal way.
The function will be run in the context of the kernel and the result returned asynchronously
to Excel.
.. note::
Any `@xloil.func` declarations prior to connection will be ignored, so part of the
jupyter notebook may need to be re-calculated to ensure functions are registed
Watching a variable in a Jupyter notebook
-----------------------------------------
The function `=xloJpyWatch(Connection, VarName)` can dynamically capture the value of any
global variable in the kernel. It is an RTD function so automatically updates when the variable
is changed.
Running code in the kernel
--------------------------
Calling `xloJpyRun` executes the provided string as python code in the kernel, captures the
result and returns it to Excel. xloJpyRun processes the code string as a `format` string using
passing the *repr* of any additional arguments, that is, it executes
`code_string.format(repr(arg1), repr(arg2), ...)`
Examples
--------
In an Excel cell enter:
::
=xloJpyRun(, "{} + {}", 3, 4)
Execute the following in a jupyter cell in a connected notebook:
::
@xloil.func
def jptest(x):
return f"Jupyter says {x}"
When xlOil connects to the kernel it will automatically import xlOil, although it does
not cause a problem if re-imported.
Now try entering `=jptest("hi")` in Excel!
Using COM Automation
--------------------
If the *jupyter* kernel has the *xloil* package installed, we can turn the tables on the
connected Excel application and control it using COM automtion. Executing the following
in the jupyter kernel will add a new workbook to the connected Excel:
::
app = xloil.app()
app.workbooks.add()
See :ref:`xlOil_Python/ExcelApplication:Introduction` for full details on the :any:`xloil.Application`
object and COM automation support.
Limitations
-----------
Functions declared in the kernel cannot specify the `async` or `rtd` arguments: they are
automatically of RTD async type to stop the kernel blocking Excel's calculation cycle!
They cannot be multi-threaded, although xlOil can connect to more than one kernel
simultaneously and exection in each kernel will be concurrent.
Registered kernel-based functions have addin/global scope in Excel. Any `@xloil.func`
declarations prior to connection will be ignored, even if `xloil` was imported.
There is reasonable overhead in the machinery required to pass the function arguments to
jupyter and process the result: all transport is via strings, so peformance degredation
may be noticable for a large number of calls or for large arrays.
================================================
FILE: docs/source/xlOil_Python/ModuleReference.rst
================================================
=================================
xlOil Python Module Reference
=================================
.. contents::
:local:
Declaring Worksheet Functions
-----------------------------
.. currentmodule:: xloil
.. autosummary::
AllowRange
Arg
Array
Cache
CannotConvert
CellError
ExcelArray
FastArray
SingleValue
func
converter
returner
import_functions
register_functions
deregister_functions
scan_module
.. automodule:: xloil
:members: Arg,Array,Cache,CannotConvert,CellError,ExcelArray,FastArray,SingleValue,func,converter,returner,import_functions,register_functions,deregister_functions,scan_module
:imported-members:
:undoc-members:
.. autodata:: AllowRange
Excel Object Model
------------------
.. currentmodule:: xloil
.. autosummary::
workbooks
worksheets
app
active_worksheet
active_workbook
Application
Caller
Range
Workbook
Worksheet
ExcelWindow
ExcelWindows
Workbooks
Worksheets
.. autodata:: workbooks
.. autodata:: worksheets
.. autofunction:: app
.. autofunction:: active_worksheet
.. autofunction:: active_workbook
.. autoclass:: Application
:members:
:inherited-members:
:undoc-members:
:special-members: __enter__, __exit__
.. autoclass:: Caller
:members:
:inherited-members:
:undoc-members:
.. autoclass:: Range
:members:
:inherited-members:
:undoc-members:
:special-members: __getitem__
.. autoclass:: Workbook
:members:
:inherited-members:
:undoc-members:
:special-members: __getitem__
.. autoclass:: Worksheet
:members:
:inherited-members:
:undoc-members:
:special-members: __getitem__, __setitem__
.. autoclass:: ExcelWindow
:members:
:inherited-members:
:undoc-members:
.. autoclass:: ExcelWindows
:members:
:inherited-members:
:undoc-members:
.. autoclass:: Workbooks
:members:
:inherited-members:
:undoc-members:
.. autoclass:: Worksheets
:members:
:inherited-members:
:undoc-members:
.. autoclass:: PauseExcel
:members:
.. automodule:: xloil
:members: SpecialCells
:imported-members:
:undoc-members:
RTD Functions
-------------
.. currentmodule:: xloil
.. autosummary::
RtdPublisher
RtdServer
xloil.rtd.subscribe
xloil.rtd.RtdSimplePublisher
.. autoclass:: RtdPublisher
:members:
.. autoclass:: RtdServer
:members:
.. automodule:: xloil.rtd
:members:
GUI Interaction
---------------
.. currentmodule:: xloil
.. autosummary::
StatusBar
ExcelGUI
TaskPaneFrame
RibbonControl
xloil.gui.CustomTaskPane
xloil.gui.find_task_pane
xloil.gui.qtpy.Qt_thread
xloil.gui.qtpy.QtThreadTaskPane
xloil.gui.tkinter.Tk_thread
xloil.gui.tkinter.TkThreadTaskPane
xloil.gui.wx.wx_thread
xloil.gui.wx.WxThreadTaskPane
.. autoclass:: StatusBar
:members:
.. autoclass:: ExcelGUI
:members:
.. autoclass:: TaskPaneFrame
:members:
.. autoclass:: RibbonControl
:members:
.. automodule:: xloil.gui
:members: CustomTaskPane
.. autofunction:: find_task_pane
.. automodule:: xloil.gui.qtpy
:members: Qt_thread, QtThreadTaskPane
:inherited-members:
.. automodule:: xloil.gui.tkinter
:members: Tk_thread, TkThreadTaskPane
:inherited-members:
.. automodule:: xloil.gui.wx
:members: wx_thread, WxThreadTaskPane
:inherited-members:
Events
------
.. currentmodule:: xloil.event
.. automodule:: xloil.event
.. autoclass:: Event
:members:
:special-members: __iadd__, __isub__
.. autofunction:: pause
.. autofunction:: allow
.. autodata:: AfterCalculate
.. autodata:: WorkbookOpen
.. autodata:: NewWorkbook
.. autodata:: SheetSelectionChange
.. autodata:: SheetBeforeDoubleClick
.. autodata:: SheetBeforeRightClick
.. autodata:: SheetActivate
.. autodata:: SheetDeactivate
.. autodata:: SheetCalculate
.. autodata:: SheetChange
.. autodata:: WorkbookAfterClose
.. autodata:: WorkbookRename
.. autodata:: WorkbookActivate
.. autodata:: WorkbookDeactivate
.. autodata:: WorkbookBeforeClose
.. autodata:: WorkbookBeforeSave
.. autodata:: WorkbookAfterSave
.. autodata:: WorkbookBeforePrint
.. autodata:: WorkbookNewSheet
.. autodata:: WorkbookAddinInstall
.. autodata:: WorkbookAddinUninstall
.. autodata:: XllAdd
.. autodata:: XllRemove
.. autodata:: ComAddinsUpdate
.. autodata:: PyBye
.. autodata:: UserException
Everything else
---------------
.. currentmodule:: xloil
.. autosummary::
in_wizard
get_async_loop
get_event_loop
from_excel_date
date_formats
linked_workbook
excel_state
check_abort
Address
run
run_async
call
call_async
excel_callback
cache
Addin
source_addin
xloil_addins
core_addin
xloil._core._AddinsDict
xloil._core._DateFormatList
xloil._core._LogWriter
xloil.logging.log
xloil.debug.use_debugger
.. autoclass:: ObjectCache
:members:
.. autodata:: cache
:annotation: = ObjectCache
:no-value:
.. automodule:: xloil
:members: in_wizard,get_async_loop,get_event_loop,from_excel_date,linked_workbook,source_addin,excel_state,run,run_async,call,call_async,excel_callback,source_addin,xloil_addins,core_addin,check_abort
:imported-members:
:undoc-members:
.. autoclass:: Address
:members:
.. autodata:: date_formats
:annotation: = _DateFormatList
:no-value:
.. autoclass:: ExcelState
:members:
:inherited-members:
:undoc-members:
.. autoclass:: Addin
:members:
.. autodata:: xloil_addins
:annotation: = _AddinsDict
:no-value:
.. automodule:: xloil._core
:members: _AddinsDict, _DateFormatList, _LogWriter
.. automodule:: xloil.logging
:members:
.. automodule:: xloil.debug
:members:
External libraries
------------------
.. currentmodule:: xloil
.. autosummary::
insert_cell_image
xloil.pandas.PDFrame
xloil.pillow.ReturnImage
xloil.matplotlib.ReturnFigure
.. autofunction:: insert_cell_image
.. automodule:: xloil.pandas
:members: PDFrame
.. automodule:: xloil.pillow
:members: ReturnImage
.. automodule:: xloil.matplotlib
:members: ReturnFigure
================================================
FILE: docs/source/xlOil_Python/Rtd.rst
================================================
======================
xlOil Python Async/Rtd
======================
Introduction
------------
RTD (real time data) functions are able to return values independently of Excel's calculation
cycle. The classic example of this is a stock ticker with live prices. It is easy to create
an RTD function in *xlOil_Python* -- the following gives a ticking clock:
::
import xloil, datetime, asyncio
@xloil.func
async def pyClock():
while True:
yield datetime.datetime.now()
await asyncio.sleep(2)
Note that calculation must be on automatic mode or you will not see the updates.
Whatever parameter is passed to `sleep` the clock will not tick faster than 2 seconds - this due
to the `RTD throttle interval `_
Which can bed changed via `xlo.app().RTD.ThrottleInterval = `, however
reducing it below the default of 2000 may impair performance. The change is global and
persists when Excel is restarted, so give some consideration to altering the value.
Any `async def` function is handled in xlOil as using RTD by default. It is possible to
use Excel's native async support, but this has some drawbacks, discussed in :any:`concepts-rtd-async`
There is another advantage of RTD: RTD functions can be 'local', i.e. called through a
VBA stub associated with the workbook, which avoids cluttering the global function namespace.
Improving RTD performance (specifying topics)
---------------------------------------------
Registering an `async def` function as described above has a certain overhead: excel will
call the function multiple times to fetch the result, so xlOil must store and compare all
the function arguments to figure out if Excel wants the result of a previous calculation
or to starta new calculation with new arguments.
If an RTD `topic`, i.e. a unique string identifier, is easy to determine we can take over
responsibility for generating it manually.
::
# First create a new RTD COM server so the `topic` strings don't collide
_rtdServer = xlo.RtdServer()
@xloil.func
def pyClock2(secs):
async def fetch() -> dt.datetime:
while True:
await asyncio.sleep(secs)
yield dt.datetime.now()
return xloil.rtd.subscribe(_rtdServer, "Time:" + str(secs), fetch)
The `subscribe` call will look for an existing publisher, i.e. a clock with `secs` interval,
and return the value if one is found. Otherwise it will run the coroutine and publish
the value. Note the coroute specifies a return type: this is handled with a return converter
just like functions decorated with :any:`xloil.func`.
The instance of the :obj:`xloil.RtdServer` object creates and registers a COM class. When xlOil is
unloaded, the server will be unregistered and destroyed. Since we created our own server the
server, we can choose the convention for these topic strings (i.e. the unique publisher ID).
Note that we do not need to declare the outer function async, the `subscribe()` call notifies
Excel that this function should be treated as RTD.
xlOil's RTD Interface
---------------------
If even finer-grained control of the RTD mechanism is required (such as in the `xloil_jupyter`
module, :doc:`Jupyter`), we can specify the publisher as described below.
We will follow the *UrlGetter* example in :doc:`ExampleRTD`. In this case we make the topics URLs.
The RTD workflow is to first check if a given topic has a publisher using `peek()`. If not,
we spin one up with :any:`xloil.RtdServer.start`. Then we :any:`xloil.RtdServer.subscribe` to
the topic which tells xlOil to call Excel's RTD function.
::
_rtdServer = xlo.RtdServer()
@xlo.func
def pyGetUrlLive(url):
if _rtdServer.peek(url) is None:
publisher = UrlGetter(url)
_rtdServer.start(publisher)
return _rtdServer.subscribe(url)
The publisher is the class which does the work. Its `connect()` method is called when a
worksheet function calls `subscribe()` for its topic. The publisher should then start
an async task to publish values.
If the worksheet function is subsequently changed or deleted, then `disconnect()` is called.
When a publisher has no subscribers it should save CPU cycles by stopping its task. A
publisher should also stop when requested by the `stop()` method.
Apart from `connect()` the remaining methods are boilerplate at least for a simple publisher.
The boilerplate can be avoided by use of the `RtdSimplePublisher` class, then only the
`run()` method in the below requires definition.
::
class UrlGetter(xlo.RtdPublisher):
def __init__(self, url):
# You *must* call this ctor explicitly or the python binding library will crash
super().__init__()
self._url = url
self._task = None
def connect(self, num_subscribers):
if self.done():
async def run():
try:
while True:
data = await getUrlAsync(self._url);
_rtdServer.publish(self._url, data)
await asyncio.sleep(4)
except Exception as e:
_rtdServer.publish(self._url, e)
self._task = xlo.get_event_loop().create_task(run())
def disconnect(self, num_subscribers):
if num_subscribers == 0:
self.stop()
# Returning True schedules the publisher for destruction
return True
def stop(self):
if self._task is not None:
self._task.cancel()
def done(self):
return self._task is None or self._task.done()
def topic(self):
return self._url
The final task, left as an exercise, is to write `getUrlAsync()`: an async function which
fetches a URL. It is straightforward with the `aiohttp` library.
================================================
FILE: docs/source/xlOil_Python/TypeConversion.rst
================================================
============================
xlOil Python Type Conversion
============================
.. contents::
:local:
Argument Types
--------------
xlOil function declarations in python look like:
::
@xlo.func
def DoSomething(x, y:float):
return x
If no type is specified for an argument, xlOil will dynamically choose a type based
on the argument provied by Excel, this can be one of:
* *bool*
* *int*
* *str*
* *float*
* *numpy.ndarray* (if an array or range is passed)
* :py:class:`xloil.CellError`
Using ``typing`` annotations improves performance at the expense of static
typing. Annonations also allow for user-defined conversion to any python type.
xlOil has built-in support for the following annotations:
.. list-table:: Supported argument annotations
:widths: 20 50
:header-rows: 1
* - Type
- Comment
* - *bool*
-
* - *int*
-
* - *str*
-
* - *float*
-
* - *numpy.ndarray*
- Use the :py:class:`xloil.Array` annotation rather than ndarray directly
* - *dict*
- Requires a 2-column input array. The first column is interpreted as keys
* - *tuple*
- Gives a tuple of tuple-of-tuples depending on number of input dimensions
* - *datetime.date*
- See :ref:`xlOil_Python/TypeConversion:Dates`
* - *datetime.datetime*
- See :ref:`xlOil_Python/TypeConversion:Dates`
* - *pandas.DataFrame*
- Can use the :py:class:`xloil.pandas.PDFrame` annotation for more conversion options.
Need to `import xloil.pandas` before use.
* - *pandas.Timestamp*
- Need to `import xloil.pandas` before use.
* - :py:class:`xloil.Range`
- See :ref:`xlOil_Python/TypeConversion:Range Arguments`
* - :py:class:`xloil.AllowRange`
- See :ref:`xlOil_Python/TypeConversion:Range Arguments`
* -
- See :ref:`xlOil_Python/TypeConversion:Custom Type Conversion`
Annotations which xlOil does not understand are ignored.
Example:
::
@xlo.func
def pySumNums(x: float, y: float, a: int = 2, b: int = 3) -> float:
return x * a + y * b
Return Types
------------
Like argument types, xlOil can read return type annotations. If no annotation
is specified xlOil tries the following conversions:
* *None*
* *int*
* *float*
* *numpy.ndarray*
* *datetime*
* :py:class:`xloil.CellError`
* *str*
* Registered custom return converters, see :ref:`xlOil_Python/TypeConversion:Custom Return Conversion`
* iterable
If none of these succeeds, the object is placed in the cache, see :ref:`xlOil_Python/TypeConversion:Cached Objects`
.. list-table:: Supported return type annotations
:widths: 20 50
:header-rows: 1
* - Type
- Comment
* - *bool*
-
* - *int*
-
* - *str*
-
* - *float*
-
* - *numpy.ndarray*
- Use the :py:class:`xloil.Array` annotation rather than ndarray directly
* - *dict*
- Outputs a 2-column array of key, value pairs
* - *tuple*
- A tuple of tuple-of-tuples produces a 1 or 2 dim array
* - *datetime.date*
- See :ref:`xlOil_Python/TypeConversion:Dates`
* - *datetime.datetime*
- See :ref:`xlOil_Python/TypeConversion:Dates`
* - *pandas.DataFrame*
- Can use the :py:class:`xloil.pandas.PDFrame` annotation for more conversion options.
Need to `import xloil.pandas` before use.
* - *pandas.Timestamp*
- Need to `import xloil.pandas` before use.
* - *PIL.Image*
- See :ref:`xlOil_Python/TypeConversion:Returning Images and Plots`
* - *matplotlib.pyplot.Figure*
- See :ref:`xlOil_Python/TypeConversion:Returning Images and Plots`
* - :py:class:`xloil.Cache`
- Placed the return value in the python object cache, see :ref:`xlOil_Python/TypeConversion:Cached Objects`.
* - :py:class:`xloil.SingleValue`
- Ensures the output will be a single cell value, not an array.
* -
- See :ref:`xlOil_Python/TypeConversion:Custom Return Conversion`
Cached Objects
--------------
If xlOil cannot convert a returned python object to Excel, it will place it in
an object dictionary and return a reference string of the form
``[SheetID]!CellNumber,#``
xlOil automatically resolves cache string passed function arguments to their
objects. With this mechanism you can pass python objects opaquely between
functions. You should not attempt to construct a cache string directly.
For example:
::
@xlo.func
def make_lambda(pow):
return lambda x: x ** pow
@xlo.func
def apply_lambda(f, x):
return f(x)
Since xlOil cannot convert a lambda function to an Excel object, it outputs a
cache reference string. That string is automatically turned back into a lambda
if passed as an argument to the second function.
The python cache is separate to the Core object cache accessed using `xloRef`
and `xloVal`. The Core cache stores native Excel objects such as arrays.
When reading functions arguments xlOil tries to lookup strings in both of these
caches.
The leading ** means xlOil can very quickly determine that a string
isn't a cache reference, so the overhead of checking if every string argument
is a cache object is very low in practice.
Using :any:`xloil.cache` it is possible to place objects into the cache. This
can be used an alternative to the :py:class:`xloil.Cache` decorator to allow
the function to choose whether or not to return a cache object. It could
also be used to return cached objects from commands or subroutines, but
understand the object lifecycle before doing this
Cache Object Lifecycle
~~~~~~~~~~~~~~~~~~~~~~
xlOil uses the caller infomation provided by Excel to construct the cache
string. When invoked from a worksheet function, the caller info contains
the sheet and cell reference and so on each calculation cycle the same
cache reference appears and the new cache object automatically overwrites
the previous one.
When invoked from a source other than a worksheet function (there are several
possibilies for this, see the help for `xlfCaller`), xlOil again generates a
reference string based on the caller info. However, this may not be unique.
In addition, objects with the same caller info will replace those created
during a previous calculation cycle. For example, creating cache objects
from a button clicked repeatedly will behave differently if Excel recalculates
in between the clicks. To override this behaviour, the exact cache `key` can
be specified. For example, use Python's `id` function or the cell address
being written to. When `key` is specified the user is responsible for managing
the lifecycle of their cache objects using `remove` in :any:`xloil.cache`.
Dates
-----
Applying the argument annotation ``datetime.datetime`` requests a date conversion. Returning
a ``datetime`` is allowed without a return annotation: the datetime will be converted to
an Excel date number:
::
from datetime import datetime, timedelta
@func
def AddDay(date: datetime):
return date + timedelta(days = 1)
xlOil can interpret strings as dates. In the settings file, the key ``DateFormats``
specifies an array of date formats to try when parsing strings. Naturally, adding more
formats decreases performance. The formats use the C++ ``std::get_time`` syntax,
see https://en.cppreference.com/w/cpp/io/manip/get_time.
Since ``std::get_time`` is **case-sensitive** on Windows, so is xlOil's date parsing
(this may be fixed in a future release as it is quite annoying for month names).
Excel has limited internal support for dates. There is no primitive date object
but cells containing numbers can be formatted as dates. This means that worksheet
functions cannot tell whether numerical values are intended as dates - this applies
to Excel built-in date functions as well. (It is possible to check for date formatting
via the COM interface but this would give behaviour inconsistent with the built-ins)
Excel does not understand timezones and neither does ``std::get_time``, so these
are currently unsupported.
Dicts
-----
When the ``dict`` *argument type* annotation is specified, xlOil expects a two-column
array of(*string*, *value*) to be passed.
Using a ``dict`` *return type* annotation allows a ``dict`` to be returned as as a
two column array. Without the annotation, the default iterable converter would be invoked,
resulting in only the keys being output.
Variable and Keyword Arguments
-------------------------------
If keyword args (`**kwargs`) are specified, xlOil expects a two-column array of
(*string*, *value*) to be passed, the same as using a ``dict`` annotation. For variable
args (`*args`) xlOil adds a large number of trailing optional arguments. The variable
argument list is ended by the first missing argument. If both *kwargs* and *args* are
specified, their order is reversed in the Excel function declaration.
The following example shows dictionary and keyword aruments:
::
@xlo.func
def pyTestKwargs(lookup: dict, **kwargs) -> dict:
lookup.update(kwargs)
return lookup
The number of trailing optional arguments is limited by the maxiumum number of arguments
allowed by Excel, which is 255 for a worksheet function and 60 for a local function.
Range Arguments
---------------
Range arguments allow a function to directly access a part of the worksheet. This
allows macro functions to write to the worksheet or it can be used for optimisation
if a function only requires a few values from a large input range.
A function can only receive range arguments if it is declared as *macro-type*. In
addition, attempting to write to a Range during Excel's calculation cycle will fail.
Annotating an argument with :py:class:`xlo.Range` will tell xlOil to pass the function an
:py:class:`Range` object, or fail if this is not possible. An :py:class:`Range`
can only be created when the input argument explicitly points to a part of the worksheet, not
an array output from another function.
Annotating an argument with :py:class:`xlo.AllowRange` will tell xlOil to pass an
:py:class:`Range` object if possible, otherwise one of the other basic data types
(int, str, array, etc.).
Custom Type Conversion
----------------------
A custom type converter is a function or a class which serialises between a set
of simple types understood by Excel and general python types.
A type converter class is expected to implement at least one of ``read(self, val)``
and ``write(self, val)`` and be decorated with :py:func:`xloil.converter`.
It may take parameters in its constructor and hold state.
A function can be interpreted as a type reader or writer depending on the parameters
passed to the :py:func:`xloil.converter` decorator.
The ``read(self, val)`` method or a function decorated as a reader or argument converter
should be able to accept a value of:
*int*, *bool*, *float*, *str*, :py:class:`xloil.ExcelArray`, :py:class:`CellError`,
:py:class:`xloil.Range` (optional)
and return a python object or raise an exception (ideally :py:class:`xloil.CannotConvert`).
An :py:class:`xloil.ExcelArray` represents an un-processed array argument, a
handle to the raw Excel object not yet converted to a *numpy* array. The converter
may opt to process only a part of this array for efficiency.
A converter may be used by name in *typing* annotations for :py:func:`xloil.func`
functions. In addition, the converter can register as the handler for a specific type
which enables that type to be used in annotations. For registration, the converter must
be default-constructible (or be a function).
By decorating with ``@xloil.converter(range=True)``, the type converter can opt to
receive :py:class:`Range` arguments in addition to the other types.
::
@xlo.converter()
def arg_doubler(x):
if isinstance(x, xlo.ExcelArray):
x = x.to_numpy()
return 2 * x
@xlo.func
def pyTestCustomConv(x: arg_doubler):
return x
@xlo.converter(typeof=bytes, register=True)
class StrToBytes:
def __init__(self, encoding='utf-8'):
self._encoding = encoding
def read(self, val):
return val.encode(self._encoding)
def write(self, val):
return val.decode(self._encoding)
@xlo.func
def Pad(text: bytes, size: int) -> StrToBytes('utf-8'):
return text.center(size)
Custom Return Conversion
------------------------
A return type converter should take a python object and return a simple type
which xlOil knows how to return to Excel. It should raise :py:class:`xloil.CannotConvert`
if it cannot handle the given object.
It can be a class implementing ``write(self, val)`` and decorated with
:py:class:`xloil.converter` or a function decorated with :py:class:`xloil.returner`
or :py:class:`xloil.converter`.
A return converter can register as the handler for a specific type which enables that
type to be used in return annotations *and* allows xlOil to try to call
the converter for Excel functions with no return annotation, see :ref:`xlOil_Python/TypeConversion:Return Types`.
::
@xlo.returner(typeof=MyType, register=True)
def mytypename(val):
return val.__name__
@xlo.func
def MakeMyType():
return MyType()
Returning Images and Plots
--------------------------
By using custom return converters you can return `PIL` or `pillow` image
objects from worksheet functions. The returned image can be automatically
sized to the calling range, or any offset from it, but it floats like a
normal picture in Excel. Calling the worksheet function again removes
the previous image and replaces it with a new one.
::
import xloil.pillow
from PIL import Image
@xlo.func(macro=True) # macro permissions required
def ShowPic(filename):
return Image.open(filename)
Importing ``xloil.pillow`` registers a custom return converter for ``PIL.Image``.
To gain control over the image size and position, use the :py:class:`xloil.pillow.ReturnImage`
return annotation.
Similarly a matplotlib figure can be returned directly
::
import xloil.matplotlib
@func(macro=True)
def Plot(x, y):
fig = pyplot.figure(figsize=(5,5))
fig.add_subplot(111).plot(x, y)
return fig
Importing ``xloil.matplotlib`` registers a custom return converter for
``matplotlib.pyplot.Figure``. To gain control over the plot size and position,
use the :py:class:`xloil.matplotlib.ReturnFigure` return annotation.
Both of these converters use :py:class:`xloil.insert_cell_image`.
================================================
FILE: docs/source/xlOil_Python/index.rst
================================================
============
xlOil Python
============
.. toctree::
:maxdepth: 4
:caption: Contents
GettingStarted
Concepts
Functions
TypeConversion
ExcelApplication
CustomGUI
Dynamic
ExternalPackages
BuiltInUDFs
Jupyter
Rtd
Debugging
DistributingAddins
Example
ExampleGUI
ExampleRTD
FAQ
ModuleReference
================================================
FILE: docs/source/xlOil_SQL/index.rst
================================================
=========
xlOil SQL
=========
The SQL plugin uses sqlite3 to provide functions which query Excel arrays (or
ranges) as if they were tables in a database. Multiple tables can be queried
and joined.
.. contents::
:local:
.. _sql-getting-started:
SQL Getting Started
-------------------
xlOil_SQL does not require any settings and is automatically loaded in a default
xlOil installation. It should appear in the plugin list in
`%APPDATA%/xlOil/xlOil.ini`:
::
Plugins=["xlOil_SQL.dll"]
You can open the example spreadsheet at :ref:`core-example-sheets` to see it in action.
For a quick demo, create a 3 column table of data in an new Excel workbook.
Make the headings 'Foo', 'Bar' and 'Baz'. The contents of the data can be
anything you like.
Suppose the table is in cells A1:C5, then in another cell type
::
=xloSql("SELECT Bar, Baz FROM Table1", A1:C5)
Make the output an array formula with Ctrl-Shift-Enter and size it
appropriately.
.. _xloSql:
xloSql
------
Executes a query on multiples data arrays
.. function:: xloSql(Query, [Meta], [Table1], [Table2], [Table3], ...)
Excecutes the SQL query on the provided tables, returning the
result in an array. The tables will be named Table1, Table2, etc in the
query but this can overrided by the `meta` parameter
Query:
a string or array of string (which will be concatenated)
describing a query in SQL (sqlite3).
Meta:
optional array of string. The first column contains the
names of the tables. Subsequent columns are interpreted
as column headings for the table. Providing a blank table
name or few names than tables results in the un-named
tables retaining their default name of tableN
TableN:
each table argument should point to an array of data with
columns as fields and records as rows. Unless column
names are specified in the meta, the first row is interpreted as column names
**Examples**
(Arguments pointing to array data are surrouned by `{}`)
::
=xloSql("SELECT table1.A, B, C FROM table1 ", { A B } , { A C } )
"INNER JOIN table2 " { Foo 1 } { Bar 2 } )
"ON table1.A == table2.A " { Baz 7 } { Foo 3 } )
--> Foo 1 3
Stateful Database Functions
---------------------------
This family of functions can be used to build up and repeatedly query an
in-memory database for cases where building the database on the fly using
:ref:`xlOil_SQL/index:xloSql` is not performant.
xloSqlDB
~~~~~~~~
.. function:: xloSqlDB()
Returns a reference to a new database object. The functions :ref:`xlOil_SQL/index:xloSqlDB`, :ref:`xlOil_SQL/index:xloSqlTable`
and :ref:`xlOil_SQL/index:xloSqlQuery` can be used to build up an in-memory database for the cases where
building these objects on the fly using :ref:`xlOil_SQL/index:xloSql` is not performant.
xloSqlTable
~~~~~~~~~~~
.. function:: xloSqlTable(Database, Data, Name, [Headings], [Query])
Creates a table in a database created with :ref:`xlOil_SQL/index:xloSqlDB`. The function returns a reference
to the database: it is recommended to chain xloSqlTable calls to force execution order
in Excel. This ensures tables are added to the database before any queries are run
Database:
a reference to a database created with `xloSqlDB`.
Data:
an array of data with columns as fields and records as rows. Unless column
headings are specified, the first row is interpreted as column names
Name:
The name of the table in the database. This must be unique.
Headings:
optional column headings for the data. If these are specified, data is read
from the first input row
Query:
An optional query to process the data as it is copied into the database.
If ommitted, "SELECT * FROM name" is used.
xloSqlQuery
~~~~~~~~~~~
.. function:: xloSqlQuery(Database, Query)
Database:
A reference to a database originally created with :ref:`xlOil_SQL/index:xloSqlDB` but which has
passed through calls to :ref:`xlOil_SQL/index:xloSqlTable`.
Query:
A SQL query to execute. Tables referenced in the query must have been added
to the database by :ref:`xlOil_SQL/index:xloSqlTable` before this function is called.
**Examples**
::
. A B C D
1 =xloSqlDB() MyTab Foo Bar
2 7 2
3 =xloSqlTable(A1, C1:D4, B1) 4 1
4 8 4
5
6 =xloSqlQuery(A3, "SELECT Bar FROM MyTab")
Cell A6 will contain the array [2, 1, 4]
xloSqlTables
~~~~~~~~~~~~
.. function:: xloSqlTables(Database)
Returns an array of all table names in the database
================================================
FILE: docs/source/xlOil_Utils.rst
================================================
===========
xlOil Utils
===========
The Utils plugin contains general purpose tools to manipulate data,
particularly arrays.
.. contents:: Contents
:local:
xloBlock: creates an array from blocks
----------------------------------------
.. function:: xloBlock(layout, arg1, arg2, arg3,...)
Creates a block matrix given a layout specification.
Layout has the form `1, 2, 3; 4, 5, 6` where numbers refer to
argument numbers provided (note indexing is 1-based). Commas
divide argument numbers, semi-colons indicate a new row. xlOil
expands the blocks row by row.
Omiting the argument number, i.e. two consecutive commas gives an
auto-sized padded space. You can only have one per row.
Whitespace is ignored in the layout specification.
Arguments may be an array or a single value, which is interpreted as
a 1x1 array.
Any holes in the result are filled with #N/A - this is preferrable
over Excel's 'empty' value which is transformed to a zero when
written to the sheet.
**Examples**
::
=xloBlock("1, 2; 3, 4", A A, B B, C C, D D)
A A B B
--> A A B B
A A B B
C C D D
=xloBlock("1, 2; 3,, 4", A A, B B, C, D)
A A B B
--> A A B B
A A B B
C - - D
xloFill: gives an array filled with a single value
--------------------------------------------------
.. function:: xloFill(value, numRows, numColumns)
Creates an array of size numRows by numColumns filled with value.
Throws if value is not a single value.
xloFillNA: replaces Empy or #N/A with a specified value
----------------------------------------------------------
.. function:: xloFillNA(value, array, [trim])
Replaces any #N/A or Empty with a specified value. #N/As in Excel
result from holes, e.g. from xloBlock or array which does not fill
the space allocated in an array formula. Empty values are particularly
problematic: they correpond to Excel reading an empty cell in an array
but are transformed to a zero when written to the sheet.
Throws if value is not a single value.
The optional trim parameter can be used to disable xlOil's default
array-trimming behaviour of resizing to the last non-empty, non-NA
row and column.
xloSort: sorts an array by multiple columns
----------------------------------------------------------
.. function:: xloSort(Array, Order, [colOrHeading1], [colOrHeading2], ...)
Sorts data by one or more column keys. The function behaves similarly
to Excel's sort command, but works as a sheet function.
The `Order` parameter should be a string with a descriptor character
for each column to used as a sort key. The character described how
the column data will be compared. Allowed characters and their meaning
are:
*a*: ascending
*A*: ascending case-sensitive,
*d*: descending
*D*: descending case-sensitive
*whitespace*: ignored
Each subsequent argument should be the (1-based) number of a column
or string. If any strings are specified, the first row of `Array`
is interpreted as column headers and the strings are matched against
these headers.
The order of the column specifiers indicates the prescendence in the
sort order.
xloSort works in-place for speed but uses Excel's variant types. This
means it will not be
**Examples**
::
=xloSort( { Baz 4 } , "a d", 2, 1)
{ Bar 4 }
{ Boz 2 }
-> 2 Boz
4 Baz
4 Bar
xloPad: pads an array to have at least two rols and columns
-----------------------------------------------------------
.. function:: xloPad(array)
xloPad works around the Excel 'feature' that duplicates arrays
with only one row or column when they are used in an array formula with
a larger target size. This can be frustrating for display of variable
sized array results. xloPad simply ensures the array has at least two
rows and columsn, adding #N/A where required to fill the space.
xloConcat: concatenates strings
--------------------------------
.. function:: xloConcat([separator], valOrArray1, valOrArray2, ...)
Concatenates strings or other values with an optional separator.
Concatenation is in argument order. The separator may be blank or any
value which can be converted to a string. Non string arguments are
converted to string before concatenation. Arrays are concatenated
along rows using the sparator if specified.
xloSplit: splits strings at a separator
---------------------------------------
.. function:: xloSplit(stringOrArray, separators, [consecutiveAsOne])
Splits a string at one or more separator characters, returning an array.
A separators can only be a single character, but multiple separators
can be specified. If `consecutiveAsOne` is omitted or TRUE, consecutive
separators are treated as one, otherwise they generate empty cells.
If a array of strings is passed, splitting will occur on each array
element and the array orientation wil be preserved. The array must be
1-dimensional
Any non string values are ignored - no coercision is performed.
The `separators` input can be a string containing any number of characters;
each will be treated as a distinct separator - multi-character separators
are not supported.
**Examples**
::
=xloSplit("Foo:Bar,,Baz", ":,")
-> Foo Bar Baz
xloIndex: gets values or sub-arrays for arrays and ranges
---------------------------------------------------------
.. function:: xloIndex(ArrayOrRef, [FromRow], [FromCol], [ToRow], [ToCol])
Extends the Excel INDEX function to support xloRefs and sub-arrays. With
the first three arguments, `xloIndex` behaves like INDEX but can be passed
an xloRef. This is an efficient way to fetch data from xloRef objects as it
avoids copying the entire object to Excel.
If *ToRow* and *ToCol* are provided they specify the right-hand end (not
inclusive) of a sub-array from (*FromRow*, *FromCol*) to (*ToRow*, *ToCol*).
**The row and column indices are 1-based**. If any of the indices is zero or
negative it is interpreted as an offset from the number of rows or columns
in the parent array.
**Examples**
::
=xloIndex(A1:D5,1,3) -> returns C1
=xloIndex(A1:D5,-1,-1) -> returns D5
=xloIndex(A1:D5,1,3,3,4) -> returns C1:D3
=xloIndex(A1:D5,-2,-2,0,0) -> returns C4:D5
================================================
FILE: docs/source/xloil.doxyfile
================================================
# Doxyfile 1.8.18
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
#
# All text after a double hash (##) is considered a comment and is placed in
# front of the TAG it is preceding.
#
# All text after a single hash (#) is considered a comment and will be ignored.
# The format is:
# TAG = value [value, ...]
# For lists, items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (\" \").
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
# This tag specifies the encoding used for all characters in the configuration
# file that follow. The default is UTF-8 which is also the encoding used for all
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
# iconv built into libc) for the transcoding. See
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
# The default value is: UTF-8.
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
# double-quotes, unless you are using Doxywizard) that should identify the
# project for which the documentation is generated. This name is used in the
# title of most generated pages and in a few other places.
# The default value is: My Project.
PROJECT_NAME = xlOil
# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
# quick idea about the purpose of the project. Keep the description short.
PROJECT_BRIEF =
# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
# in the documentation. The maximum height of the logo should not exceed 55
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
# left blank the current directory will be used.
OUTPUT_DIRECTORY = $(XLO_SOLN_DIR)/build/docs/doxygen
# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
# directories (in 2 levels) under the output directory of each output format and
# will distribute the generated files over these directories. Enabling this
# option can be useful when feeding doxygen a huge amount of source files, where
# putting all generated files in the same directory would otherwise causes
# performance problems for the file system.
# The default value is: NO.
CREATE_SUBDIRS = NO
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
# Ukrainian and Vietnamese.
# The default value is: English.
OUTPUT_LANGUAGE = English
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all generated output in the proper direction.
# Possible values are: None, LTR, RTL and Context.
# The default value is: None.
OUTPUT_TEXT_DIRECTION = None
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
# descriptions after the members that are listed in the file and class
# documentation (similar to Javadoc). Set to NO to disable this.
# The default value is: YES.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
# description of a member or function before the detailed description
#
# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
# The default value is: YES.
REPEAT_BRIEF = YES
# This tag implements a quasi-intelligent brief description abbreviator that is
# used to form the text in various listings. Each string in this list, if found
# as the leading text of the brief description, will be stripped from the text
# and the result, after processing the whole list, is used as the annotated
# text. Otherwise, the brief description is used as-is. If left blank, the
# following values are used ($name is automatically replaced with the name of
# the entity):The $name class, The $name widget, The $name file, is, provides,
# specifies, contains, represents, a, an and the.
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# doxygen will generate a detailed section even if there is only a brief
# description.
# The default value is: NO.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
# inherited members of a class in the documentation of that class as if those
# members were ordinary class members. Constructors, destructors and assignment
# operators of the base classes will not be shown.
# The default value is: NO.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
# before files name in the file list and in the header files. If set to NO the
# shortest path that makes the file name unique will be used
# The default value is: YES.
FULL_PATH_NAMES = YES
# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
# Stripping is only done if one of the specified strings matches the left-hand
# part of the path. The tag can be used to show relative paths in the file list.
# If left blank the directory from which doxygen is run is used as the path to
# strip.
#
# Note that you can specify absolute paths here, but also relative paths, which
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
STRIP_FROM_PATH =
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
# header file to include in order to use a class. If left blank only the name of
# the header file containing the class definition is used. Otherwise one should
# specify the list of include paths that are normally passed to the compiler
# using the -I flag.
STRIP_FROM_INC_PATH =
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
# less readable) file names. This can be useful is your file systems doesn't
# support long names like on DOS, Mac, or CD-ROM.
# The default value is: NO.
SHORT_NAMES = NO
# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
# first line (until the first dot) of a Javadoc-style comment as the brief
# description. If set to NO, the Javadoc-style will behave just like regular Qt-
# style comments (thus requiring an explicit @brief command for a brief
# description.)
# The default value is: NO.
JAVADOC_AUTOBRIEF = NO
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
# such as
# /***************
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
# Javadoc-style will behave just like regular comments and it will not be
# interpreted by doxygen.
# The default value is: NO.
JAVADOC_BANNER = NO
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
# line (until the first dot) of a Qt-style comment as the brief description. If
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
# requiring an explicit \brief command for a brief description.)
# The default value is: NO.
QT_AUTOBRIEF = NO
# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
# a brief description. This used to be the default behavior. The new default is
# to treat a multi-line C++ comment block as a detailed description. Set this
# tag to YES if you prefer the old behavior instead.
#
# Note that setting this tag to YES also means that rational rose comments are
# not recognized any more.
# The default value is: NO.
MULTILINE_CPP_IS_BRIEF = NO
# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
# documentation from any documented member that it re-implements.
# The default value is: YES.
INHERIT_DOCS = YES
# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
# page for each member. If set to NO, the documentation of a member will be part
# of the file/class/namespace that contains it.
# The default value is: NO.
SEPARATE_MEMBER_PAGES = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
# uses this value to replace tabs by spaces in code fragments.
# Minimum value: 1, maximum value: 16, default value: 4.
TAB_SIZE = 4
# This tag can be used to specify a number of aliases that act as commands in
# the documentation. An alias has the form:
# name=value
# For example adding
# "sideeffect=@par Side Effects:\n"
# will allow you to put the command \sideeffect (or @sideeffect) in the
# documentation, which will result in a user-defined paragraph with heading
# "Side Effects:". You can put \n's in the value part of an alias to insert
# newlines (in the resulting output). You can put ^^ in the value part of an
# alias to insert a newline as if a physical newline was in the original file.
# When you need a literal { or } or , in the value part of an alias you have to
# escape them by means of a backslash (\), this can lead to conflicts with the
# commands \{ and \} for these it is advised to use the version @{ and @} or use
# a double escape (\\{ and \\})
ALIASES =
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
# members will be omitted, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_FOR_C = NO
# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
# Python sources only. Doxygen will then generate output that is more tailored
# for that language. For instance, namespaces will be presented as packages,
# qualified scopes will look different, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_JAVA = NO
# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
# sources. Doxygen will then generate output that is tailored for Fortran.
# The default value is: NO.
OPTIMIZE_FOR_FORTRAN = NO
# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
# sources. Doxygen will then generate output that is tailored for VHDL.
# The default value is: NO.
OPTIMIZE_OUTPUT_VHDL = NO
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
# sources only. Doxygen will then generate output that is more tailored for that
# language. For instance, namespaces will be presented as modules, types will be
# separated into more groups, etc.
# The default value is: NO.
OPTIMIZE_OUTPUT_SLICE = NO
# Doxygen selects the parser to use depending on the extension of the files it
# parses. With this tag you can assign which parser to use for a given
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
# tries to guess whether the code is fixed or free formatted code, this is the
# default for Fortran type files). For instance to make doxygen treat .inc files
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
# use: inc=Fortran f=C.
#
# Note: For files without extension you can use no_extension as a placeholder.
#
# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
# the files are not read by doxygen.
EXTENSION_MAPPING =
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
# documentation. See https://daringfireball.net/projects/markdown/ for details.
# The output of markdown processing is further processed by doxygen, so you can
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
# case of backward compatibilities issues.
# The default value is: YES.
MARKDOWN_SUPPORT = YES
# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
# to that level are automatically included in the table of contents, even if
# they do not have an id attribute.
# Note: This feature currently applies only to Markdown headings.
# Minimum value: 0, maximum value: 99, default value: 5.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
TOC_INCLUDE_HEADINGS = 5
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
# globally by setting AUTOLINK_SUPPORT to NO.
# The default value is: YES.
AUTOLINK_SUPPORT = YES
# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
# to include (a tag file for) the STL sources as input, then you should set this
# tag to YES in order to let doxygen match functions declarations and
# definitions whose arguments contain STL classes (e.g. func(std::string);
# versus func(std::string) {}). This also make the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
# The default value is: NO.
BUILTIN_STL_SUPPORT = NO
# If you use Microsoft's C++/CLI language, you should set this option to YES to
# enable parsing support.
# The default value is: NO.
CPP_CLI_SUPPORT = NO
# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
# will parse them like normal C++ but will assume all classes use public instead
# of private inheritance when no explicit protection keyword is present.
# The default value is: NO.
SIP_SUPPORT = NO
# For Microsoft's IDL there are propget and propput attributes to indicate
# getter and setter methods for a property. Setting this option to YES will make
# doxygen to replace the get and set methods by a property in the documentation.
# This will only work if the methods are indeed getting or setting a simple
# type. If this is not the case, or you want to show the methods anyway, you
# should set this option to NO.
# The default value is: YES.
IDL_PROPERTY_SUPPORT = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
# The default value is: NO.
DISTRIBUTE_GROUP_DOC = NO
# If one adds a struct or class to a group and this option is enabled, then also
# any nested class or struct is added to the same group. By default this option
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
# type (e.g. under the Public Functions section). Set it to NO to prevent
# subgrouping. Alternatively, this can be done per class using the
# \nosubgrouping command.
# The default value is: YES.
SUBGROUPING = YES
# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
# are shown inside the group in which they are included (e.g. using \ingroup)
# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
# and RTF).
#
# Note that this feature does not work in combination with
# SEPARATE_MEMBER_PAGES.
# The default value is: NO.
INLINE_GROUPED_CLASSES = NO
# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
# with only public data fields or simple typedef fields will be shown inline in
# the documentation of the scope in which they are defined (i.e. file,
# namespace, or group documentation), provided this scope is documented. If set
# to NO, structs, classes, and unions are shown on a separate page (for HTML and
# Man pages) or section (for LaTeX and RTF).
# The default value is: NO.
INLINE_SIMPLE_STRUCTS = NO
# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
# enum is documented as struct, union, or enum with the name of the typedef. So
# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
# with name TypeT. When disabled the typedef will appear as a member of a file,
# namespace, or class. And the struct will be named TypeS. This can typically be
# useful for C code in case the coding convention dictates that all compound
# types are typedef'ed and only the typedef is referenced, never the tag name.
# The default value is: NO.
TYPEDEF_HIDES_STRUCT = NO
# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
# cache is used to resolve symbols given their name and scope. Since this can be
# an expensive process and often the same symbol appears multiple times in the
# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
# doxygen will become slower. If the cache is too large, memory is wasted. The
# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
# symbols. At the end of a run doxygen will report the cache usage and suggest
# the optimal cache size from a speed point of view.
# Minimum value: 0, maximum value: 9, default value: 0.
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
# documentation are documented, even if no documentation was available. Private
# class members and static file members will be hidden unless the
# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
# Note: This will also disable the warnings about undocumented members that are
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
# The default value is: NO.
EXTRACT_PRIVATE = NO
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
# methods of a class will be included in the documentation.
# The default value is: NO.
EXTRACT_PRIV_VIRTUAL = NO
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
# scope will be included in the documentation.
# The default value is: NO.
EXTRACT_PACKAGE = NO
# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
# included in the documentation.
# The default value is: NO.
EXTRACT_STATIC = NO
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO,
# only classes defined in header files are included. Does not have any effect
# for Java sources.
# The default value is: YES.
EXTRACT_LOCAL_CLASSES = NO
# This flag is only useful for Objective-C code. If set to YES, local methods,
# which are defined in the implementation section but not in the interface are
# included in the documentation. If set to NO, only methods in the interface are
# included.
# The default value is: NO.
EXTRACT_LOCAL_METHODS = NO
# If this flag is set to YES, the members of anonymous namespaces will be
# extracted and appear in the documentation as a namespace called
# 'anonymous_namespace{file}', where file will be replaced with the base name of
# the file that contains the anonymous namespace. By default anonymous namespace
# are hidden.
# The default value is: NO.
EXTRACT_ANON_NSPACES = NO
# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
# undocumented members inside documented classes or files. If set to NO these
# members will be included in the various overviews, but no documentation
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = YES
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# declarations. If set to NO, these declarations will be included in the
# documentation.
# The default value is: NO.
HIDE_FRIEND_COMPOUNDS = NO
# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
# documentation blocks found inside the body of a function. If set to NO, these
# blocks will be appended to the function's detailed documentation block.
# The default value is: NO.
HIDE_IN_BODY_DOCS = NO
# The INTERNAL_DOCS tag determines if documentation that is typed after a
# \internal command is included. If the tag is set to NO then the documentation
# will be excluded. Set it to YES to include the internal documentation.
# The default value is: NO.
INTERNAL_DOCS = NO
# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
# names in lower-case letters. If set to YES, upper-case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# (including Cygwin) ands Mac users are advised to set this option to NO.
# The default value is: system dependent.
CASE_SENSE_NAMES = NO
# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
# their full class and namespace scopes in the documentation. If set to YES, the
# scope will be hidden.
# The default value is: NO.
HIDE_SCOPE_NAMES = NO
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
# YES the compound reference will be hidden.
# The default value is: NO.
HIDE_COMPOUND_REFERENCE= NO
# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
# the files that are included by a file in the documentation of that file.
# The default value is: YES.
SHOW_INCLUDE_FILES = YES
# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
# grouped member an include statement to the documentation, telling the reader
# which file to include in order to use the member.
# The default value is: NO.
SHOW_GROUPED_MEMB_INC = NO
# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
# files with double quotes in the documentation rather than with sharp brackets.
# The default value is: NO.
FORCE_LOCAL_INCLUDES = NO
# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
# documentation for inline members.
# The default value is: YES.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
# (detailed) documentation of file and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order.
# The default value is: YES.
SORT_MEMBER_DOCS = YES
# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
# descriptions of file, namespace and class members alphabetically by member
# name. If set to NO, the members will appear in declaration order. Note that
# this will also influence the order of the classes in the class list.
# The default value is: NO.
SORT_BRIEF_DOCS = NO
# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
# (brief and detailed) documentation of class members so that constructors and
# destructors are listed first. If set to NO the constructors will appear in the
# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
# member documentation.
# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
# detailed member documentation.
# The default value is: NO.
SORT_MEMBERS_CTORS_1ST = NO
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
# of group names into alphabetical order. If set to NO the group names will
# appear in their defined order.
# The default value is: NO.
SORT_GROUP_NAMES = NO
# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
# fully-qualified names, including namespaces. If set to NO, the class list will
# be sorted only by class name, not including the namespace part.
# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
# Note: This option applies only to the class list, not to the alphabetical
# list.
# The default value is: NO.
SORT_BY_SCOPE_NAME = NO
# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
# type resolution of all parameters of a function it will reject a match between
# the prototype and the implementation of a member function even if there is
# only one candidate or it is obvious which candidate to choose by doing a
# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
# accept a match between prototype and implementation in such cases.
# The default value is: NO.
STRICT_PROTO_MATCHING = NO
# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
# list. This list is created by putting \todo commands in the documentation.
# The default value is: YES.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
# list. This list is created by putting \test commands in the documentation.
# The default value is: YES.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
# list. This list is created by putting \bug commands in the documentation.
# The default value is: YES.
GENERATE_BUGLIST = YES
# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
# the deprecated list. This list is created by putting \deprecated commands in
# the documentation.
# The default value is: YES.
GENERATE_DEPRECATEDLIST= YES
# The ENABLED_SECTIONS tag can be used to enable conditional documentation
# sections, marked by \if ... \endif and \cond
# ... \endcond blocks.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
# initial value of a variable or macro / define can have for it to appear in the
# documentation. If the initializer consists of more lines than specified here
# it will be hidden. Use a value of 0 to hide initializers completely. The
# appearance of the value of individual variables and macros / defines can be
# controlled using \showinitializer or \hideinitializer command in the
# documentation regardless of this setting.
# Minimum value: 0, maximum value: 10000, default value: 30.
MAX_INITIALIZER_LINES = 30
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
# the bottom of the documentation of classes and structs. If set to YES, the
# list will mention the files that were used to generate the documentation.
# The default value is: YES.
SHOW_USED_FILES = YES
# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
# will remove the Files entry from the Quick Index and from the Folder Tree View
# (if specified).
# The default value is: YES.
SHOW_FILES = YES
# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
# page. This will remove the Namespaces entry from the Quick Index and from the
# Folder Tree View (if specified).
# The default value is: YES.
SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
# the version control system). Doxygen will invoke the program by executing (via
# popen()) the command command input-file, where command is the value of the
# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
# by doxygen. Whatever the program writes to standard output is used as the file
# version. For an example see the documentation.
FILE_VERSION_FILTER =
# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
# by doxygen. The layout file controls the global structure of the generated
# output files in an output format independent way. To create the layout file
# that represents doxygen's defaults, run doxygen with the -l option. You can
# optionally specify a file name after the option, if omitted DoxygenLayout.xml
# will be used as the name of the layout file.
#
# Note that if you run doxygen from a directory containing a file called
# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
# tag is left empty.
LAYOUT_FILE =
# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
# the reference definitions. This must be a list of .bib files. The .bib
# extension is automatically appended if omitted. This requires the bibtex tool
# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated to
# standard output by doxygen. If QUIET is set to YES this implies that the
# messages are off.
# The default value is: NO.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
# this implies that the warnings are on.
#
# Tip: Turn warnings on while writing the documentation.
# The default value is: YES.
WARNINGS = YES
# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: YES.
WARN_IF_UNDOCUMENTED = YES
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some parameters
# in a documented function, or documenting parameters that don't exist or using
# markup commands wrongly.
# The default value is: YES.
WARN_IF_DOC_ERROR = YES
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
# are documented, but have no documentation for their parameters or return
# value. If set to NO, doxygen will only warn about wrong or incomplete
# parameter documentation, but not about the absence of documentation. If
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# The default value is: NO.
WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
# The default value is: NO.
WARN_AS_ERROR = NO
# The WARN_FORMAT tag determines the format of the warning messages that doxygen
# can produce. The string should contain the $file, $line, and $text tags, which
# will be replaced by the file and line number from which the warning originated
# and the warning text. Optionally the format may contain $version, which will
# be replaced by the version of the file (if it could be obtained via
# FILE_VERSION_FILTER)
# The default value is: $file:$line: $text.
WARN_FORMAT = "$file:$line: $text"
# The WARN_LOGFILE tag can be used to specify a file to which warning and error
# messages should be written. If left blank the output is written to standard
# error (stderr).
WARN_LOGFILE =
#---------------------------------------------------------------------------
# Configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag is used to specify the files and/or directories that contain
# documented source files. You may enter file names like myfile.cpp or
# directories like /usr/src/myproject. Separate the files or directories with
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = $(XLO_SOLN_DIR)/include/xloil
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
# possible encodings.
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# read by doxygen.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.c \
*.cc \
*.cxx \
*.cpp \
*.c++ \
*.java \
*.ii \
*.ixx \
*.ipp \
*.i++ \
*.inl \
*.idl \
*.ddl \
*.odl \
*.h \
*.hh \
*.hxx \
*.hpp \
*.h++ \
*.cs \
*.d \
*.php \
*.php4 \
*.php5 \
*.phtml \
*.inc \
*.m \
*.markdown \
*.md \
*.mm \
*.dox \
*.doc \
*.txt \
*.py \
*.pyw \
*.f90 \
*.f95 \
*.f03 \
*.f08 \
*.f18 \
*.f \
*.for \
*.vhd \
*.vhdl \
*.ucf \
*.qsf \
*.ice
# The RECURSIVE tag can be used to specify whether or not subdirectories should
# be searched for input files as well.
# The default value is: NO.
RECURSIVE = NO
# The EXCLUDE tag can be used to specify files and/or directories that should be
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
#
# Note that relative paths are relative to the directory from which doxygen is
# run.
EXCLUDE =
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
# directories that are symbolic links (a Unix file system feature) are excluded
# from the input.
# The default value is: NO.
EXCLUDE_SYMLINKS = NO
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories for example use the pattern */test/*
EXCLUDE_PATTERNS =
# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
# (namespaces, classes, functions, etc.) that should be excluded from the
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# AClass::ANamespace, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS = msxll \
toml \
std \
detail \
spdlog
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
# *.h) to filter out the source-files in the directories. If left blank all
# files are included.
EXAMPLE_PATTERNS = *
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude commands
# irrespective of the value of the RECURSIVE tag.
# The default value is: NO.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or directories
# that contain images that are to be included in the documentation (see the
# \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command:
#
#
#
# where is the value of the INPUT_FILTER tag, and is the
# name of an input file. Doxygen will then use the output that the filter
# program writes to standard output. If FILTER_PATTERNS is specified, this tag
# will be ignored.
#
# Note that the filter must not add or remove lines; it is applied before the
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
INPUT_FILTER =
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
# filter if there is a match. The filters are a list of the form: pattern=filter
# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
# patterns match the file name, INPUT_FILTER is applied.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
FILTER_PATTERNS =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will also be used to filter the input files that are used for
# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
# The default value is: NO.
FILTER_SOURCE_FILES = NO
# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
# it is also possible to disable source filtering for a specific pattern using
# *.ext= (so without naming a filter).
# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
FILTER_SOURCE_PATTERNS =
# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
# is part of the input, its contents will be placed on the main page
# (index.html). This can be useful if you have a project on for instance GitHub
# and want to reuse the introduction page also for the doxygen output.
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# Configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
# generated. Documented entities will be cross-referenced with these sources.
#
# Note: To get rid of all source code in the generated output, make sure that
# also VERBATIM_HEADERS is set to NO.
# The default value is: NO.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# classes and enums directly into the documentation.
# The default value is: NO.
INLINE_SOURCES = NO
# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
# special comment blocks from generated source code fragments. Normal C, C++ and
# Fortran comments will always remain visible.
# The default value is: YES.
STRIP_CODE_COMMENTS = YES
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
# entity all documented functions referencing it will be listed.
# The default value is: NO.
REFERENCED_BY_RELATION = NO
# If the REFERENCES_RELATION tag is set to YES then for each documented function
# all documented entities called/used by that function will be listed.
# The default value is: NO.
REFERENCES_RELATION = NO
# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
# to YES then the hyperlinks from functions in REFERENCES_RELATION and
# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
# link to the documentation.
# The default value is: YES.
REFERENCES_LINK_SOURCE = YES
# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
# source code will show a tooltip with additional information such as prototype,
# brief description and links to the definition and documentation. Since this
# will make the HTML file larger and loading of large files a bit slower, you
# can opt to disable this feature.
# The default value is: YES.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
SOURCE_TOOLTIPS = YES
# If the USE_HTAGS tag is set to YES then the references to source code will
# point to the HTML generated by the htags(1) tool instead of doxygen built-in
# source browser. The htags tool is part of GNU's global source tagging system
# (see https://www.gnu.org/software/global/global.html). You will need version
# 4.8.6 or higher.
#
# To use it do the following:
# - Install the latest version of global
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
# - Make sure the INPUT points to the root of the source tree
# - Run doxygen as normal
#
# Doxygen will invoke htags (and that will in turn invoke gtags), so these
# tools must be available from the command line (i.e. in the search path).
#
# The result: instead of the source browser generated by doxygen, the links to
# source code will now point to the output of htags.
# The default value is: NO.
# This tag requires that the tag SOURCE_BROWSER is set to YES.
USE_HTAGS = NO
# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
# verbatim copy of the header file for each class for which an include is
# specified. Set to NO to disable this.
# See also: Section \class.
# The default value is: YES.
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
# If clang assisted parsing is enabled you can provide the clang parser with the
# path to the compilation database (see:
# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
# were built. This is equivalent to specifying the "-p" option to a clang tool,
# such as clang-check. These options will then be passed to the parser.
# Note: The availability of this option depends on whether or not doxygen was
# generated with the -Duse_libclang=ON option for CMake.
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
# compounds will be generated. Enable this if the project contains a lot of
# classes, structs, unions or interfaces.
# The default value is: YES.
ALPHABETICAL_INDEX = YES
# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
# which the alphabetical index list will be split.
# Minimum value: 1, maximum value: 20, default value: 5.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
# while generating the index headers.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
# The default value is: YES.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
# it.
# The default directory is: html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_OUTPUT = html/doxygen
# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
# generated HTML page (for example: .htm, .php, .asp).
# The default value is: .html.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FILE_EXTENSION = .html
# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
# each generated HTML page. If the tag is left blank doxygen will generate a
# standard header.
#
# To get valid HTML the header file that includes any scripts and style sheets
# that doxygen needs, which is dependent on the configuration options used (e.g.
# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
# default header using
# doxygen -w html new_header.html new_footer.html new_stylesheet.css
# YourConfigFile
# and then modify the file new_header.html. See also section "Doxygen usage"
# for information on how to generate the default header that doxygen normally
# uses.
# Note: The header is subject to change so you typically have to regenerate the
# default header when upgrading to a newer version of doxygen. For a description
# of the possible markers and block names see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
# generated HTML page. If the tag is left blank doxygen will generate a standard
# footer. See HTML_HEADER for more information on how to generate a default
# footer and what special commands can be used inside the footer. See also
# section "Doxygen usage" for information on how to generate the default footer
# that doxygen normally uses.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FOOTER =
# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
# sheet that is used by each HTML page. It can be used to fine-tune the look of
# the HTML output. If left blank doxygen will generate a default style sheet.
# See also section "Doxygen usage" for information on how to generate the style
# sheet that doxygen normally uses.
# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
# it is more robust and this tag (HTML_STYLESHEET) will in the future become
# obsolete.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefore more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
# other source files which should be copied to the HTML output directory. Note
# that these files will be copied to the base HTML output directory. Use the
# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
# files will be copied as-is; there are no commands or markers available.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a colorwheel, see
# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
# purple, and 360 is red again.
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_HUE = 220
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use grayscales only. A
# value of 255 will produce the most vivid colors.
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_SAT = 100
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
# gradually make the output lighter, whereas values above 100 make the output
# darker. The value divided by 100 is the actual gamma applied, so 80 represents
# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
# change the gamma.
# Minimum value: 40, maximum value: 240, default value: 80.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
# consists of multiple levels of tabs that are statically embedded in every HTML
# page. Disable this option to support browsers that do not have JavaScript,
# like the Qt help browser.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_MENUS = YES
# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
# documentation will contain sections that can be hidden and shown after the
# page has loaded.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_DYNAMIC_SECTIONS = NO
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
# such a level that at most the specified number of entries are visible (unless
# a fully collapsed tree already exceeds this amount). So setting the number of
# entries 1 will produce a full collapsed tree by default. 0 is a special value
# representing an infinite number of entries and will result in a full expanded
# tree by default.
# Minimum value: 0, maximum value: 9999, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_INDEX_NUM_ENTRIES = 100
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
# generated that can be used as input for Apple's Xcode 3 integrated development
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
# Makefile in the HTML output directory. Running make will produce the docset in
# that directory and running make install will install the docset in
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
# genXcode/_index.html for more information.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_DOCSET = NO
# This tag determines the name of the docset feed. A documentation feed provides
# an umbrella under which multiple documentation sets from a single provider
# (such as a company or product suite) can be grouped.
# The default value is: Doxygen generated docs.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_FEEDNAME = "Doxygen generated docs"
# This tag specifies a string that should uniquely identify the documentation
# set bundle. This should be a reverse domain-name style string, e.g.
# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_BUNDLE_ID = org.doxygen.Project
# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
# the documentation publisher. This should be a reverse domain-name style
# string, e.g. com.mycompany.MyDocSet.documentation.
# The default value is: org.doxygen.Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
# The default value is: Publisher.
# This tag requires that the tag GENERATE_DOCSET is set to YES.
DOCSET_PUBLISHER_NAME = Publisher
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
# Windows.
#
# The HTML Help Workshop contains a compiler that can convert all HTML output
# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
# files are now used as the Windows 98 help format, and will replace the old
# Windows help format (.hlp) on all Windows platforms in the future. Compressed
# HTML files also contain an index, a table of contents, and you can search for
# words in the documentation. The HTML workshop also contains a viewer for
# compressed HTML files.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_HTMLHELP = NO
# The CHM_FILE tag can be used to specify the file name of the resulting .chm
# file. You can add a path in front of the file if the result should not be
# written to the html output directory.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_FILE =
# The HHC_LOCATION tag can be used to specify the location (absolute path
# including file name) of the HTML help compiler (hhc.exe). If non-empty,
# doxygen will try to run the HTML help compiler on the generated index.hhp.
# The file has to be specified with full path.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
# (YES) or that it should be included in the master .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
GENERATE_CHI = NO
# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
# and project file content.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated
# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members to
# the table of contents of the HTML help documentation and to the tree view.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
TOC_EXPAND = NO
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
# (.qch) of the generated HTML documentation.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_QHP = NO
# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
# the file name of the resulting .qch file. The path specified is relative to
# the HTML output folder.
# This tag requires that the tag GENERATE_QHP is set to YES.
QCH_FILE =
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
# Project output. For more information please see Qt Help Project / Namespace
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_NAMESPACE = org.doxygen.Project
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
# Help Project output. For more information please see Qt Help Project / Virtual
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
# folders).
# The default value is: doc.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_VIRTUAL_FOLDER = doc
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
# filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_NAME =
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
# custom filter to add. For more information please see Qt Help Project / Custom
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
# filters).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_CUST_FILTER_ATTRS =
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
# project's filter section matches. Qt Help Project / Filter Attributes (see:
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
# This tag requires that the tag GENERATE_QHP is set to YES.
QHP_SECT_FILTER_ATTRS =
# The QHG_LOCATION tag can be used to specify the location of Qt's
# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
# generated .qhp file.
# This tag requires that the tag GENERATE_QHP is set to YES.
QHG_LOCATION =
# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
# generated, together with the HTML files, they form an Eclipse help plugin. To
# install this plugin and make it available under the help contents menu in
# Eclipse, the contents of the directory containing the HTML and XML files needs
# to be copied into the plugins directory of eclipse. The name of the directory
# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
# After copying Eclipse needs to be restarted before the help appears.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_ECLIPSEHELP = NO
# A unique identifier for the Eclipse help plugin. When installing the plugin
# the directory name containing the HTML and XML files should also have this
# name. Each documentation set should have its own identifier.
# The default value is: org.doxygen.Project.
# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
ECLIPSE_DOC_ID = org.doxygen.Project
# If you want full control over the layout of the generated HTML pages it might
# be necessary to disable the index and replace it with your own. The
# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
# of each HTML page. A value of NO enables the index and the value YES disables
# it. Since the tabs in the index contain the same information as the navigation
# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
DISABLE_INDEX = NO
# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
# structure should be generated to display hierarchical information. If the tag
# value is set to YES, a side panel will be generated containing a tree-like
# index structure (just like the one that is generated for HTML Help). For this
# to work a browser that supports JavaScript, DHTML, CSS and frames is required
# (i.e. any modern browser). Windows users are probably better off using the
# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
# further fine-tune the look of the index. As an example, the default style
# sheet generated by doxygen has an example that shows how to put an image at
# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
# the same information as the tab index, you could consider setting
# DISABLE_INDEX to YES when enabling this option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
GENERATE_TREEVIEW = YES
# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
# doxygen will group on one line in the generated HTML documentation.
#
# Note that a value of 0 will completely suppress the enum values from appearing
# in the overview section.
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
ENUM_VALUES_PER_LINE = 4
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
# Minimum value: 0, maximum value: 1500, default value: 250.
# This tag requires that the tag GENERATE_HTML is set to YES.
TREEVIEW_WIDTH = 250
# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
# external symbols imported via tag files in a separate window.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
EXT_LINKS_IN_WINDOW = NO
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
# the HTML output. These images will generally look nicer at scaled resolutions.
# Possible values are: png The default and svg Looks nicer but requires the
# pdf2svg tool.
# The default value is: png.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_FORMULA_FORMAT = png
# Use this tag to change the font size of LaTeX formulas included as images in
# the HTML documentation. When you change the font size after a successful
# doxygen run you need to manually remove any form_*.png images from the HTML
# output directory to force them to be regenerated.
# Minimum value: 8, maximum value: 50, default value: 10.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
FORMULA_MACROFILE =
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
# https://www.mathjax.org) which uses client side JavaScript for the rendering
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
# installed or if you want to formulas look prettier in the HTML output. When
# enabled you may also need to install MathJax separately and configure the path
# to it using the MATHJAX_RELPATH option.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
USE_MATHJAX = NO
# When MathJax is enabled you can set the default output format to be used for
# the MathJax output. See the MathJax site (see:
# http://docs.mathjax.org/en/latest/output.html) for more details.
# Possible values are: HTML-CSS (which is slower, but has the best
# compatibility), NativeMML (i.e. MathML) and SVG.
# The default value is: HTML-CSS.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_FORMAT = HTML-CSS
# When MathJax is enabled you need to specify the location relative to the HTML
# output directory using the MATHJAX_RELPATH option. The destination directory
# should contain the MathJax.js script. For instance, if the mathjax directory
# is located at the same level as the HTML output directory, then
# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
# Content Delivery Network so you can quickly see the result without installing
# MathJax. However, it is strongly recommended to install a local copy of
# MathJax from https://www.mathjax.org before deployment.
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_EXTENSIONS =
# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
# of code that will be used on startup of the MathJax code. See the MathJax site
# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
# example see the documentation.
# This tag requires that the tag USE_MATHJAX is set to YES.
MATHJAX_CODEFILE =
# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
# the HTML output. The underlying search engine uses javascript and DHTML and
# should work on any modern browser. Note that when using HTML help
# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
# there is already a search function so this one should typically be disabled.
# For large projects the javascript based search engine can be slow, then
# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
# search using the keyboard; to jump to the search box use + S
# (what the is depends on the OS and browser, but it is typically
# , /