While most people would stick with python and Qt to build modern user interfaces (GUIs), I recently found an ancient language, tcl/tk, existed well before the birth of python, and yet still used today in the industry, quietly. It's royalty-free, available in all python distributions, as well as Linux and mingw64. Despite its availability, it's not popular, partly because it's superceded by python, which is much more beginner-friendly. Though, with its speed, small foot-print (less than 10Mb for the barebone core), I decide to turn into tcl/tk for building user interfaces and interactive graphical programs for my own scientific projects. I already knew there exist vtk for scientific plotting and 3D displays, but it dropped support for tcl/tk since version 8, again due to the unpopularity of tcl/tk language. Luckily, vtk still host their 7.1 source code, and that version appear to be the last to support tcl/tk without python. so, I set out to build vtk 7.1 to call its functions directly in tcl, and of course, encountered many roadblocks. Lucky for me, I was able to punch through all the roadblocks, one by one, until I was able to do it. This git repository is intended to host the fixed codes of vtk-7.1 and instruction of how to build vtk-7.1 from scratch with tcl/tk support, in DYNAMIC library form, using Windows's msys2-mingw64 and Linux's gcc.
Enough of my saga, here is the instruction of how to build vtk 7.1 from source with tcl/tk support, in dynamic library form:
- Download and extract the vtk 7.1.1 source code from vtk official website: https://www.vtk.org/files/release/7.1/VTK-7.1.1.zip
- Download and extract all the files and folders in this git repository (except this readme, mind you).
- Copy and replace all the extracted files and folders from this git repository into the extracted vtk 7.1.1 source directory.
- In Windows, install msys2 from here, and follow all the steps to install core packages for coding (e.g. base-devel, mingw-w64-x86_64-toolchain) https://www.msys2.org/
- Download and install cmake (actually, cmake-gui interface). https://cmake.org/download/ Special notes for Linux build-from-source: need to install extra package to fix cmake gui configure error:
- mesa-libGL-devel.x86_64
- libXt-devel.x86_64 (need mesa-dri-drivers.x86_64 for 3D/OpenGL display across X-forwarding anyways)
- Start Cmake. Special notes for Windows: When using cmake the first time, make sure to configure for MSYS (but still run "make" under mingw64 afterward. There are several msys window, one is msys2-msys, and one is msys2-mingw64. Make sure to use msys2-mingw64 to run "make" command)
- Set CMAKE_BUILD_TYPE to Release (of course...)
- Make a new directory for installation instead of the default C:/Program Files (x86)/VTK under "CMAKE_INSTALL_PREFIX"
- To compile for TCLTK, tick "VTK_Group_Tk" and "VTK_WRAP_TCL". Skip python. Then hit Configure.
- Make sure to build tcl/tk from source: https://www.tcl.tk/software/tcltk/download.html, with make install-private-headers after make;make install Alternative: download and install pre-built tcl/tk with tcl3d and a bunch of good extras from bawt website and point the TCL/TK include path there (in cmakegui). http://www.bawt.tcl3d.org/preview.html
- Next, specify TCL and TK include/library path (should be in the <tcl/tk_install_directyr>/include and /lib). VTK_USE_TK is automatically set to ON. Tick "Advanced" to show and set the definition of TK and TCL include/lib directory. Disclaimer: Do NOT use the one bundled with mingw64 (stupid mingw64 tcl/tk include separation into "generic" and "win", and then some outside - we need them in THE SAME PLACE!!!). Else the vtk application will crash/quit for no reason, especially right after a vtkTkRenderWidget initialization - which is the WHOLE POINT of vtk integration to tcl/tk.
- Change TK_INTERNAL_PATH to be the same with TK_INCLUDE_PATH (after make install-private-headers)
- Finally, click "Generate"
- Cd to the build directory:
cd <build_directory>
For Windows, make sure to run msys2-mingw64 instead of msys2-msys console. To speed up, type:
make -j8
for 8 thread parallel build. Type:
make <specific_vtk_module>
e.g.
make vtkRenderingLabel
To make a specific module only (in case something went wrong and you don't want the output to be flooded with other module's building logs. 15. Once it's done, type:
make install
The vtk modules for tcl are located in <install_dir>/lib/tcltk/vtk-7.1. The compiled dynamic libraries are in <install_dir>/bin. Simply copy this whole vtk-7.1 folder into the tcl or tk lib directory, where all other tcl/tk module directories are located, and make sure the dynamic libraries are in PATH and LD_LIBRARY_PATH. The easiest way is to copy those dynamic libraries into the same directory where tclsh.exe and/or wish.exe are located. To use vtk in wish/tcl, type package require vtk
Below is my notes of all the pains and sufferings I encountered during vtk 7.1 build for tcl, and how to fix them.
20211208, 12:03pm:
- Need to change a macro (again) to make sure linux understand it's a dynamic library export: File VTKGenerateExportHeader.cmake, in C:\msys64\mingw64\vtk\VTK-7.1.1\CMake: Change line:
elseif(COMPILER_HAS_HIDDEN_VISIBILITY AND USE_COMPILER_HIDDEN_VISIBILITY)
to:
else()
Most modern compiler has this flag (visibility), but somehow cmake failed to recognize this my linux system's gcc.
20211208: finalized guide for vtk rebuild using msys2 mingw64 Windows 10 1809. At first, I have to modify some file after cmake, but now I know where to modify before cmake config. Also, I was able to fix some weird compilation error for 3rd party libs that I couldn't before, and also figured out why vtk broke in tcl vtkTkRenderWidget initialization. It was tough, but totally worth it. I'm sure there are more compiler warning that I didn't catch, but I'll leave that for the audience.
- Modify C:\msys64\mingw64\vtk\VTK-7.1.1\Common\Core\vtkAOSDataArrayTemplate.h: Add "VTKCOMMONCORE_EXPORT" to FastDownCast definition:
VTKCOMMONCORE_EXPORT static vtkAOSDataArrayTemplate* FastDownCast(vtkAbstractArray *source){
- Put these lines in C:\msys64\mingw64\vtk\VTK-7.1.1\Rendering\OpenGL2\vtkWin32RenderWindowInteractor.cxx inside a "#ifndef _WIN32" macro check, like this:
#ifndef _WIN32
typedef struct _TOUCHINPUT {
LONG x;
LONG y;
HANDLE hSource;
DWORD dwID;
DWORD dwFlags;
DWORD dwMask;
DWORD dwTime;
ULONG_PTR dwExtraInfo;
DWORD cxContact;
DWORD cyContact;
} TOUCHINPUT, *PTOUCHINPUT;
DECLARE_HANDLE(HTOUCHINPUT);
#define HTOUCHINPUT ULONG
#endif
These are already defined in winuser.h of mingw. Possibly a Linux remnant.
- In C:\msys64\mingw64\vtk\VTK-7.1.1\Rendering\Label\vtkLabelHierarchyPrivate.h: add "const" to this line like this:
bool operator () ( const vtkIdType& a, const vtkIdType& b ) const
and in C:\msys64\mingw64\vtk\VTK-7.1.1\Rendering\Label\vtkLabelHierarchy.cxx: add "const" to this line like this:
bool operator()(const vtkHierarchyNode & a,
const vtkHierarchyNode & b) const
Both comparators' bool operator() definition must be set to const. Damn, this one is tough to trace... Source: https://stackoverflow.com/questions/51235355/comparison-object-being-invocable-as-const
- Modify C:\msys64\mingw64\vtk\VTK-7.1.1\ThirdParty\tiff\vtktiff\CMakeLists.txt, line 493 (or find FILE_OFFSET_BITS). Change:
if (UNIX)
to:
if (UNIX OR MINGW)
This will remove the compile error "left hand of == is empty" for FILE_OFFSET_BITS in stdio.h
- Another tough-to-track bug is in C:\msys64\mingw64\vtk\VTK-7.1.1\ThirdParty\libxml2\vtklibxml2\include\libxml\xmlexports.h: within the section of:
#if defined(_WIN32) && defined(MINGW32)
Change this line:
#define XMLPUBVAR __declspec(dllexport)
To:
#define XMLPUBVAR __declspec(dllexport) extern
This will fix "multiple definition" compilation error caused by, again, mismanagement of C-C++ linkage. Source: https://mail.gnome.org/archives/xml/2010-May/msg00026.html
- In C:\msys64\mingw64\vtk\VTK-7.1.1\Common\DataModel\vtkPolyData.h, move the whole section of:
inline unsigned char vtkPolyData::GetCellPoints(){}
up, above where the first time it was used.
Simply search for GetCellPoints(
and find the first line of code where it was first used (yes, USED, not just declared)
Hint: it's in inline int vtkPolyData::IsTriangle(int v1, int v2, int v3)
for vtk 7.1:
this->GetCellPoints(cells[j], n2, tVerts2);
So, move the definition of inline unsigned char vtkPolyData::GetCellPoints(){}
above IsTriangle
function
This will suppress the annoying warning of redefined without dllimport shit...
- Modify vtkexportheader.cmake.in under C:\msys64\mingw64\vtk\VTK-7.1.1\CMake: Basically we need to remove the reference to EXMPORT_IMPORT_CONDITION and DEFINE_IMPORT. I really hate when EXPORT macros means dllimport. If it's static, just leave it, don't dllexport or dllimport it!!!
# ifndef @EXPORT_MACRO_NAME@
# define @EXPORT_MACRO_NAME@ @DEFINE_EXPORT@
# endif
This will ensure everything is built as DLL.
- Need to change pkgIndex.tcl.in under C:\msys64\mingw64\vtk\VTK-7.1.1\Wrapping\Tcl\ to make it load a dll library in the PATH:
proc load_library_package {libName libPath {libPrefix {@VTK_TCL_LIBNAME_PREFIX@}}} {
to
proc load_library_package {libName {libPrefix {@VTK_TCL_LIBNAME_PREFIX@}}} {
Change:
set libFile [file join $libPath "$libPrefix$libName-@VTK_MAJOR_VERSION@.@VTK_MINOR_VERSION@$libExt"]
to
set libFile "$libPrefix$libName-@VTK_MAJOR_VERSION@.@VTK_MINOR_VERSION@$libExt"
Change:
if {[catch "cd {$libPath}; load {$libFile}" errorMessage]} {
to
if {[catch "load {$libFile}" errorMessage]} {
Change
::vtk::init::load_library_package {vtk${kit}TCL} {@VTK_TCL_LIBRARY_DIR@}
to
::vtk::init::load_library_package {vtk${kit}TCL}
This will skip the stupid library search path and instead use the default "load" function with simple pure library file name without path. Always work.