From 16c77dc28df419a7ff677b52202339f2c97ebd1c Mon Sep 17 00:00:00 2001 From: Alexander Zhirov Date: Fri, 16 May 2025 01:35:01 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BD=D0=BE=D0=B2=D1=8B=D0=B9=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D1=8C=20=D0=B4=D0=B5=D0=BC=D0=BE=D0=BD=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=B0=D1=86=D0=B8=D0=B8=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=BA?= =?UTF-8?q?=D1=82=D0=BD=D0=BE-=D0=BE=D1=80=D0=B8=D0=B5=D0=BD=D1=82=D0=B8?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=B4=D1=85=D0=BE=D0=B4=D0=B0=20=D0=BA=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8E=20=D0=B0=D0=BD=D0=B8=D0=BC?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D0=B8=20=D1=81=D0=BF=D0=B8=D0=BD=D0=BD=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE=D0=BB?= =?UTF-8?q?=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 19 +++--- source/app.d | 5 +- source/examples/shell/README.md | 4 ++ source/examples/shell/ospinner.d | 109 +++++++++++++++++++++++++++++++ source/examples/shell/package.d | 1 + source/examples/version_.d | 2 +- 6 files changed, 129 insertions(+), 11 deletions(-) create mode 100644 source/examples/shell/ospinner.d diff --git a/README.md b/README.md index b6760a9..e3a230e 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,14 @@ ```sh d-examples -├── common Общего назначения -│ ├── isexists Проверяет наличие исполняемого файла в директориях,указанных в переменной окружения PATH -│ └── splittext Форматирует массив строк, разбивая их на строки указанной длины -├── ncurses Использование библиотеки ncurses -│ ├── menu Интерактивное консольное меню -│ └── password Консольное окно для ввода пароля -└── shell Запуск команд в shell - ├── pipe Чтение выходных данных на примере ip - └── spinner Эмуляция статуса выполнения процесса +├── common # Общего назначения +│ ├── isexists # Проверяет наличие исполняемого файла в директориях,указанных в переменной окружения PATH +│ └── splittext # Форматирует массив строк, разбивая их на строки указанной длины +├── ncurses # Использование библиотеки ncurses +│ ├── menu # Интерактивное консольное меню +│ └── password # Консольное окно для ввода пароля +└── shell # Запуск команд в shell + ├── pipe # Чтение выходных данных на примере ip + ├── spinner # Эмуляция статуса выполнения процесса + └── ospinner # Демонстрирует объектно-ориентированный подход к созданию анимации спиннера в консоли ``` diff --git a/source/app.d b/source/app.d index 7f8330b..82117e0 100644 --- a/source/app.d +++ b/source/app.d @@ -20,6 +20,8 @@ int main(string[] args) .add(new Command("shell", "Запуск команд в shell") .add(new Command("pipe", "Чтение выходных данных на примере ip")) .add(new Command("spinner", "Эмуляция статуса выполнения процесса")) + .add(new Command("ospinner", + "Демонстрирует объектно-ориентированный подход к созданию анимации спиннера в консоли")) ) .parse(args); @@ -34,7 +36,8 @@ int main(string[] args) }) .on("shell", (shell) { shell .on("pipe", (pipe) { pipeShell(); }) - .on("spinner", (loading) { spinnerShell(); }); + .on("spinner", (spinner) { spinnerShell(); }) + .on("ospinner", (ospinner) { oSpinnerShell(); }); }); return EXIT_SUCCESS; diff --git a/source/examples/shell/README.md b/source/examples/shell/README.md index 1b1aa94..d920848 100644 --- a/source/examples/shell/README.md +++ b/source/examples/shell/README.md @@ -7,3 +7,7 @@ ## spinner Функция `spinnerShell` создаёт анимацию спиннера в консоли, используя брайлевские символы для имитации процесса обработки. Устанавливает обработчик сигнала `Ctrl+C` для корректного завершения с восстановлением курсора. Выполняет цикл с задержкой 100 мс, отображая вращающийся символ, и завершает выполнение с выводом "Done!". Использует модули `std.stdio`, `core.thread`, `core.time` и функции C для работы с сигналами и выводом. + +## ospinner + +Функция `oSpinnerShell` демонстрирует объектно-ориентированный подход к созданию анимации спиннера в консоли. Использует абстрактный класс `Command` для выполнения задач с анимацией спиннера (брайлевские символы). Класс `LongRunningCommand` имитирует длительную задачу (5 секунд). Обработчик `Ctrl+C` завершает выполнение с восстановлением курсора. Анимация отображает имя команды и сообщение, завершаясь выводом "Done!". Использует модули `std.stdio`, `core.thread`, `core.time`, `core.stdc.signal`, `core.stdc.stdio`, `core.stdc.stdlib`. diff --git a/source/examples/shell/ospinner.d b/source/examples/shell/ospinner.d new file mode 100644 index 0000000..f73f577 --- /dev/null +++ b/source/examples/shell/ospinner.d @@ -0,0 +1,109 @@ +module examples.shell.ospinner; + +import std.stdio; +import core.thread; +import core.time; +import core.stdc.signal : signal, SIGINT; +import core.stdc.stdio : fprintf; +import core.stdc.stdlib : exit; + +// Обработчик сигнала Ctrl+C +extern (C) void handleCtrlC(int sig) nothrow @nogc +{ + // Используем fprintf для вывода в stderr + fprintf(core.stdc.stdio.stderr, "\033[?25h\r\033[KInterrupted!\n"); + exit(0); +} + +// Абстрактный базовый класс для команд +private abstract class Command +{ + private string message = "Processing..."; // Сообщение для спиннера + private string name = "Default command"; // Наименование команды + private immutable dchar[] spinner = [ + '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' + ]; // Символы спиннера + + // Абстрактный метод, который должны реализовать наследники + protected abstract void execute(); + + this(string commandName) + { + name = commandName; + } + + // Метод для запуска команды с анимацией спиннера + final void run() + { + ulong i = 0; + bool taskCompleted = false; + + // Создаём поток для выполнения команды + auto worker = new Thread({ + try + { + execute(); // Выполняем логику команды + } + catch (Exception e) + { + stderr.writefln("Error: %s", e.msg); + } + taskCompleted = true; + }); + + // Скрываем курсор + write("\033[?25l"); + stdout.flush(); + + // Запускаем задачу + worker.start(); + + // Цикл анимации спиннера + while (!taskCompleted) + { + writef("\r%s: %s %c", name, message, spinner[i]); + stdout.flush(); + i = (i + 1) % spinner.length; + Thread.sleep(dur!("msecs")(100)); + } + + // Ждём завершения потока + worker.join(); + + // Восстанавливаем курсор и очищаем строку + writef("\033[?25h\r\033[K%s: Done!\n", name); + } + + // Метод для установки пользовательского сообщения + void setMessage(string msg) + { + message = msg; + } +} + +// Пример команды с выводом +private class LongRunningCommand : Command +{ + this(string commandName) + { + super(commandName); + } + + override void execute() + { + Thread.sleep(dur!("seconds")(5)); // Имитация работы + } +} + +void oSpinnerShell() +{ + // Устанавливаем обработчик для SIGINT (Ctrl+C) + signal(SIGINT, &handleCtrlC); + + // Создаём команду + auto cmd = new LongRunningCommand("Test"); + + cmd.setMessage("Working on task..."); + writeln("Running with spinner:"); + cmd.run(); +} diff --git a/source/examples/shell/package.d b/source/examples/shell/package.d index 3ea1e93..54a667a 100644 --- a/source/examples/shell/package.d +++ b/source/examples/shell/package.d @@ -2,3 +2,4 @@ module examples.shell; public import examples.shell.pipe; public import examples.shell.spinner; +public import examples.shell.ospinner; diff --git a/source/examples/version_.d b/source/examples/version_.d index 373c0e0..a6a7618 100644 --- a/source/examples/version_.d +++ b/source/examples/version_.d @@ -1,3 +1,3 @@ module examples.version_; -enum examplesVersion = "0.4.0"; +enum examplesVersion = "0.5.0";