Osmanip – это библиотека C++, предоставляющая полезные механизмы для работы с управляющими последовательностями ANSI и настройки потока вывода программ. С помощью этих механизмов вы можете оформлять выводимые строки различными цветами и стилями, изменять расположение курсора в терминале и регулировать прочие компоненты вроде индикаторов выполнения и графики. Весь этот функционал будет очень полезен для придания желаемого вида общему потоку вывода программы или для выполнения операций с курсором.


Если вы захотите использовать это ПО в своём проекте или упомянуть в статье, будьте добры, указывайте на его ссылку.

Если у вас возникнет желание внести свой вклад в репозиторий, сначала прочтите этот файл.

Документация кода сгенерирована при помощи Doxygen и доступна здесь. Помимо этого, есть ещё справочная страница GitHub, которая содержит дополнительные практические инструкции и множество примеров.

Вот некоторые из них:


Цвета и стили


Индикаторы выполнения


Двухмерная графика в терминале

Поддерживаемые ОС:

  • Linux;
    • Ubuntu (протестировано);
  • Windows (версия 10 и выше);
    • Cygwin64 (протестировано);
    • MSYS2 (протестировано);
    • MinGW (протестировано);
    • WSL (протестировано);
  • MacOS.

▍ Новости последних релизов


  • добавлен класс OS_Decorator для удобства управления стилизацией потока вывода программы;
  • добавлена справочная страница библиотеки;
  • добавлена полная поддержка Windows и MacOS;

Список возможностей


▍ Механизмы работы с escape-последовательностями ANSI


Управление цветом и стилем:

#include <iostream>
#include <osmanip/manipulators/colsty.hpp>

// Вывод красной строки
std::cout << osm::feat( osm::col, "red" ) <<
"This string is red!" << osm::feat( osm::rst, "color" );

// Вывод жирной строки
std::cout << osm::feat( osm::sty, "red" ) <<
"This string is bold!" << osm::feat( osm::rst, "bd/ft" );

Управление курсором:

#include <iostream>
#include <osmanip/manipulators/cursor.hpp>

// Перемещение курсора вправо на два пробела
std::cout << osm::feat( osm::crs, "right", 2 ) << "Cursor moved!";

Управляющие последовательности для терминала:

#include <iostream>
#include <osmanip/manipulators/cursor.hpp>

// Вывод звука колокольчика
std::cout << osm::feat( osm::tcs, "bell" );

Дополнительные функции:

#include <iostream>
#include <osmanip/manipulators/printer.hpp>

// Аналог Python функции "print"
osm::print( std::cout, "This is the ", "\"print\" ",
            "function for the normal output stream! ", 100, "% working!" );

Класс для управления стилизацией вывода программы:

#include <iostream>
#include <osmanip/manipulators/printer.hpp>

osm::OS_Decorator my_shell;

// Изменение предопределённого стиля и цвета std::cout
my_shell.setColor( "green", std::cout );
my_shell.setStyle( "underlined", std::cout );

my_shell( std::cout ) << "The stdout stream has been changed
                          using the OS_Decorator class!" << "\n";

// Изменение предопределённого стиля и цвета std::cerr
my_shell.setColor( "red", std::cerr );
my_shell.setStyle( "bold italics", std::cerr ); // Примечание: добавлено 2 стиля

my_shell( std::cerr ) << "The stderr stream has been changed
                          using the OS_Decorator class!" << "\n";

Дополнительные примеры и инструкции можно найти здесь.

Почему для работы с escape-последовательностями стоит выбрать именно эту библиотеку:

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

▍ Индикаторы выполнения


Процентный индикатор:

#include <iostream>
#include <osmanip/progressbar/progressbar.hpp>
#include <osmanip/utility/options.hpp>

osm::ProgressBar<int> percentage_bar;

percentage_bar.setMin( 5 );
percentage_bar.setMax ( 46 );
percentage_bar.setStyle( "indicator", "%" );

std::cout << "This is a normal percentage bar: " << "\n";
osm::OPTION( osm::CURSOR::OFF ); // Сокрытие курсора для лучшей отрисовки вывода
 for ( int i = percentage_bar.getMin(); i < percentage_bar.getMax(); i++ )
  {
   percentage_bar.update( i );
   //Выполнение операций...
  }
osm::OPTION( osm::CURSOR::ON );


Индикатор загрузки:

#include <iostream>
#include <osmanip/progressbar/progressbar.hpp>
#include <osmanip/utility/options.hpp>

osm::ProgressBar<int> loading_bar( 3, 25 );

loading_bar.setStyle( "loader", "#" );
loading_bar.setBrackets( "{", "}" );
loading_bar.setMessage( "processing..." );

std::cout << "This is a normal loading bar: " << "\n";
osm::OPTION( osm::CURSOR::OFF ); // Сокрытие курсора для лучшей отрисовки вывода
for ( int i = loading_bar.getMin(); i < loading_bar.getMax(); i++ )
 {
  loading_bar.update( i );
  //Выполнение операций...
 }
osm::OPTION( osm::CURSOR::ON );


Смешанный прогресс бар:

#include <iostream>
#include <osmanip/progressbar/progressbar.hpp>
#include <osmanip/utility/options.hpp>

osm::ProgressBar<int> progress_bar( 3, 25 );

progress_bar.setStyle( "complete", "%", "■" );
progress_bar.setBrackets( "[", "]" );
progress_bar.setMessage( "elaborating..." );
progress_bar.setRemainingTimeFlag( "on" );
progress_bar.setColor( "red" );

std::cout << "This is a mixed progress bar with color
              and time remaining info: " << "\n";
osm::OPTION( osm::CURSOR::OFF ); // Сокрытие курсора для лучшей отрисовки вывода
for ( int i = progress_bar.getMin(); i < progress_bar.getMax(); i++ )
 {
  progress_bar.update( i );
  //Выполнение операций...
 }
osm::OPTION( osm::CURSOR::ON );


Спиннер:

#include <iostream>
#include <osmanip/progressbar/progressbar.hpp>
#include <osmanip/utility/options.hpp>

osm::ProgressBar<int> spinner;

spinner.setMin( 2 );
spinner.setMax ( 33 );
spinner.setStyle( "spinner", "/-\\|" );

std::cout << "This is a progress spinner: " << "\n";
osm::OPTION( osm::CURSOR::OFF ); // Сокрытие курсора для лучшей отрисовки вывода
for ( int i = spinner.getMin(); i < spinner.getMax(); i++ )
 {
  spinner.update( i );
  //Выполнение операций...
 }
osm::OPTION( osm::CURSOR::ON );


Дополнительные примеры и инструкции можно найти здесь.

Преимущества использования именно этой библиотеки для реализации индикаторов выполнения:

  • крайняя простота применения;
  • поддерживает положительные и отрицательные переменные любого стандартного типа (integer, float, double и прочих);
  • в качестве максимума и минимума можно установить любые нужные значения, и индикатор будет построен относительно них;
  • каждый элемент индикатора настраивается (сообщения, стиль, цвет, тип скобок, оставшееся время и т.д.). Также можно использовать вместо смешанного прогресс бара только индикацию выполнения или загрузки.
  • потокобезопасность, то есть можно одновременно использовать множество индикаторов выполнения.

▍ Графика в терминале


Создание анимаций:

#include <osmanip/manipulators/colsty.hpp>
#include <osmanip/graphics/canvas.hpp>

osm::Canvas canvas(10,10);

canvas.setBackground( '.', osm::feat( osm::col, "bg white" ) +
                      osm::feat( osm::col, "black" ) );
std::cout << "Display an animation in a canvas\n";

for( uint i = 0; i < 10; i++ )
 {
  canvas.clear();
  canvas.put( 0, 2, 'x' );
  canvas.put( i, 3, 'A', osm::feat( osm::col, "red" ) );
  canvas.put( 5, 0, 'B', osm::feat( osm::col, "blue" ) );
  canvas.put( 7, 8, 'Z', osm::feat( osm::col, "bg cyan" ) +
              osm::feat( osm::col, "black" ) + osm::feat( osm::sty, "bold" ) );
  canvas.refresh();
 }


Построение 2D-графиков:

#include <functional>
#include <osmanip/manipulators/colsty.hpp>
#include <osmanip/graphics/canvas.hpp>

osm::Plot2DCanvas plot_2d_canvas( 50, 20 );

std::cout << "\n" << "Plot2DCanvas with sin and cos" << "\n";
plot_2d_canvas.setBackground( ' ', osm::feat( osm::col, "bg white" ) );
plot_2d_canvas.enableFrame( true );
plot_2d_canvas.setFrame( osm::FrameStyle::BOX,
osm::feat( osm::col, "bg white" ) + osm::feat( osm::col, "black" ) );
plot_2d_canvas.enableFrame( true );
plot_2d_canvas.setFrame( osm::FrameStyle::BOX,
osm::feat( osm::col, "bg white" ) + osm::feat( osm::col, "black" ) );
plot_2d_canvas.setScale( 1/3.14, 0.2) ;

for( float i = 0; i < 40; i++ )
 {
  plot_2d_canvas.setOffset( i/3.14, -2 );
  plot_2d_canvas.clear();
  plot_2d_canvas.draw( std::function <float( float )>( []( float x ) ->
                       float{ return std::cos( x ); } ), 'X',
                       osm::feat( osm::col, "bg white" ) +
                       osm::feat( osm::col, "bd red" ) );
  plot_2d_canvas.draw( std::function <float( float )>( []( float x ) ->
                       float{ return std::sin( x ); } ), 'X',
                       osm::feat( osm::col, "bg white" ) +
                       osm::feat( osm::col, "bd blue" ) );
  plot_2d_canvas.refresh();
  sleep_for( milliseconds( 100 ) );
 }


Дополнительные примеры и инструкции можно найти здесь.

Преимущества реализации графики в терминале с помощью этой библиотеки:

  • подобный функционал обеспечивается очень немногими библиотеками C++, и эта одна из них;
  • высокий уровень кастомизируемости;
  • более шустрая и удобная альтернатива отрисовке графиков простых функций без потребности в GUI.

▍ Дополнительная поддержка UNICODE и ANSI в Windows


// Активация escape-последовательностей ANSI
osm::OPTION( osm::ANSI::ON );
// выполнение нужных действий...
osm::OPTION( osm::ANSI::ON );


// Активация символов unicode
osm::OPTION( osm::UNICODECH::ON );
// выполнение нужных действий...
osm::OPTION( osm::UNICODECH::ON );

Дополнительные примеры и инструкции можно найти здесь.

Установка и использование


▍ Установка


1. Скачать один из релизов репозитория.

2. Распаковать архив и открыть каталог.

3. Установить и скомпилировать библиотеку с зависимостями при помощи установочного скрипта.

./script/install.sh

Этот скрипт поддерживает установку под Ubuntu, MacOS, Windows и прочими операционными системами.

Примечание: если вы работаете в Cygwin64, то можете получить ошибку, связанную с символом \r. Чтобы её избежать, предварительно выполните для скрипта команду dos2unix.

В результате в каталоге /usr/lib создаётся новая библиотека, а файлы заголовков устанавливаются по пути /usr/include.

Примечание: если вы работаете в MacOS или Windows, пути будут несколько отличаться (см. install.sh).

Обязательные программы (устанавливаются автоматически скриптом):


Необязательные программы, для разработчиков:

  • Valgrind для запуска скрипта debug.sh;
  • Cppcheck для запуска скрипта debug.sh;
  • Clang formatter для подготовки кода к пул-реквестам;
  • wget для скачивания дополнительных репозиториев зависимостей;
  • unzip для распаковки архивных каталогов во время скачивания и установки;
  • Doxygen для генерации документации при разработке;
  • doctest для тестирования;
  • hurry.filesize для скрипта size_of_dir.py;
  • termcolor для скрипта size_of_dir.py.

4. Дополнительно: для периодического обновления репозитория:

./scripts/update.sh
./scripts/install.sh

5. Дополнительно: для деинсталляции репозитория из системы:

./scripts/uninstall.sh

▍ Использование на вашем устройстве


Для использования одного или более заголовков библиотеки:

#include <osmanip/module_folder/module_name.hpp>

В случае использования библиотеки в программе добавьте флаг -losmanip для линковки исходного кода.

Примечание: не забудьте также добавить флаг -pthread, если хотите использовать потокозависимые библиотеки вроде progressbar/multi_progress_bar.hpp.

▍ Компиляция примеров и тестирование


Компиляция примеров:

make main

Запуск примеров:

./bin/manipulators
./bin/progressbar
./bin/graphics

Примечание: если вы работаете в Windows, исполняемые файлы оканчиваются на .exe.

Компиляция тестов:

make tests

Запуск тестов:

./bin/tests

Также есть вариант вернуться обратно в состояние до компиляции. Для этого просто выполните:

make clean

▍ Полезные скрипты


Я добавил кое-какие дополнительные полезные скрипты в каталог scripts. После компиляции исходного кода их можно будет запустить из домашнего каталога репозитория.

Скрипт debug.sh используется для запуска отладчиков Valgrind и Cppcheck для всего кода.

Valgrind можно запустить через отдельный исполняемый файл:

./scripts/debug_cpp.sh [valgrind-tool-name] [executable-name]

Структура репозитория
osmanip/
├── .github/
│   ├── workflows/
│   │   ├── DocGenerator.yml
│   │   ├── codeql-analysis.yml
├── img/
├── include/
│   ├── graphics/
│   │   ├── canvas.hpp
│   │   ├── plot_2D.hpp
│   ├── manipulators/
│   │   ├── colsty.hpp
│   │   ├── cursor.hpp
│   │   ├── common.hpp
│   │   ├── printer.hpp
│   ├── progressbar/
│   │   ├── progress_bar.hpp
│   │   ├── multi_progress_bar.hpp
│   ├── utility/
│   │   ├── windows.hpp
│   │   ├── options.hpp
├── src/
│   ├── graphics/
│   │   ├── canvas.cpp
│   │   ├── plot_2D.cpp
│   ├── manipulators/
│   │   ├── colsty.cpp
│   │   ├── cursor.cpp
│   │   ├── common.cpp
│   │   ├── printer.hpp
│   ├── progressbar/
│   │   ├── progress_bar.cpp
│   │   ├── multi_progress_bar.cpp
│   ├── utility/
│   │   ├── windows.cpp
├── examples/
│   ├── manipulators.cpp
│   ├── progressbar.cpp
│   ├── graphics.cpp
├── scripts/
│   ├── debug.sh
│   ├── install.sh
│   ├── uninstall.sh
│   ├── update.sh
│   ├── size_of_dir.py
├── test/
│   ├── graphics/
│   │   ├── tests_canvas.cpp
│   │   ├── tests_plot_2D.cpp
│   ├── manipulators/
│   │   ├── tests_common.cpp
│   │   ├── tests_colsty.cpp
│   │   ├── tests_cursor.cpp
│   │   ├── tests_printer.cpp
│   ├── progressbar/
│   │   ├── tests_progress_bar.cpp
│   │   ├── tests_multi_progress_bar.cpp
│── README.md
│── CONTRIBUTING.md
│── LICENSE
│── CITATION.cff
│── Makefile
│── Doxyfile
│── .gitignore
│── .clang-format
│── .valgrindrc
│── .gitignore
│── .all-contributorsrc


▍ Дальнейшие планы


  • Работа с escape-последовательностями ANSI:
    • добавить механизмы работы с символами UNICODE;
    • добавить в класс OS_decorator новые методы;
    • реализовать перенаправление файлов при работе с выводом.
  • Индикаторы выполнения:
    • добавить метод elapsedTime() для отображения пройденного времени выполнения, заменив им существующий метод getTime();
  • Графика для терминала:
    • добавить метод для сопровождения графиков легендой;
    • добавить автоматическое изменение размера графика;
    • добавить опцию отображения осей;
    • расширить 2D графику до 3D;
  • Системный функционал:
    • улучшить компиляцию с помощью CMake;
    • сравнить библиотеку с аналогами на основе бенчмарков и прочих исследований.

Комментарии (5)


  1. DrMefistO
    05.08.2022 19:07
    +7

    Переизобретение ncurses, как по мне.


    1. nagayev
      06.08.2022 13:32

      это да, но тут удобнее


  1. panteleymonov
    06.08.2022 01:38
    +3

    М-да, теперь тетрис в килобайты не написать - буст нужен!


    1. Hisoka
      06.08.2022 06:29
      +2

      Если C++17 я ещё как-то понять могу, то буст - увольте.


      1. NN1
        06.08.2022 07:35

        Ради того, что можно было бы и на коленке сделать:

        #include <boost/preprocessor/seq/for_each.hpp>#include <boost/preprocessor/tuple/to_seq.hpp>

        BOOST_PP_SEQ_FOR_EACH( PROGRESSBAR, _, ARGS( int, long, long long, double, long double, float ) ); }