4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

python標準ライブラリにあった対話型UIフレームワーク紹介する

Posted at

こんにちは、超久しぶりに記事を書く六角レンチです。
今日は標準ライブラリを見ていたら使えそうなライブラリを見つけたので、紹介したいと思います。

紹介するやつ

簡単に言うと、対話型UIのフレームワークです。
iwctlみたいな感じのものが作れそう

今回はこれを使って、マクドナルドの略称か判別する対話型UIアプリを作ろうと思います(謎)

略称か判別する関数

マクドナルドの略称か判別する関数を作ります。
といっても昔作ったので、コピペしてきました

def is_macdonald_ryakusyou(ryakusyou: str) -> bool:
    MACDONALD = "マクドナルド"
    if type(ryakusyou) is not str:
        print("文字列ちゃうやんけ")
    elif len(ryakusyou) == 0:
        print("空文字やんけ")
    else:
        index = 0
        for i in MACDONALD:
            if ryakusyou[index] == i:
                index += 1
                if index == len(ryakusyou):
                    break
        else:
            return False
        return True

試しにマクドを入れてみると...
image.png
ちゃんと動きます

対話型UI化

判別する関数が動くことを確認できたので、いよいよ対話型UIを作っていきます

from cmd import Cmd


class MacdonaldCheckShell(Cmd):
    def do_macdonaldcheck(self, ryakusyou: str) -> None:
        """マクドナルドの略称か判別するコマンド"""
        MACDONALD = "マクドナルド"
        if type(ryakusyou) is not str:
            self.error("文字列ちゃうやんけ")

        elif len(ryakusyou) == 0:
            self.error("空文字やんけ")

        else:
            index = 0
            for i in MACDONALD:
                if ryakusyou[index] == i:
                    index += 1
                    if index == len(ryakusyou):
                        break
            else:
                print(f"略称: [{ryakusyou}]は不正。")
                return
            print(f"略称: [{ryakusyou}]は正しい。")

    def do_exit(self, _: str) -> bool:
        """終了するコマンド"""
        print("exit...")
        return True

    @staticmethod
    def error(*text: str) -> None:
        print("Error: " + "\n".join(text))


if __name__ == "__main__":
    MacdonaldCheckShell().cmdloop()

staticmethodのerrorはエラー表示をするために作った

コマンドの作り方は関数名をdo_{コマンド名}って感じにすることでできる
また、コマンドの関数の返り値としてTrueを返すことで対話型UIを終了させることが可能

docstring(関数の下に書いてる"""で囲んでる文字列)がヘルプメッセージになる

関数の引数はコマンドの引数が入る...と思ったら入力された文字のコマンド以降の部分が入る
例えば、test honi hoyaが入力されるとdo_test関数の第一引数にhoni hoyaの文字列が入ってくる
前後の空白文字は消されるので入力された文字.lstrip(コマンド名).strip()された物が引数に来ると考えるといいかも(ちょっと使いにくいような...)

実行してみた

image.png

help コマンド名でヘルプメッセージを表示できる

Tabキーによるコマンド推測、上下矢印キーのコマンド履歴も使用可能だった(すごい)

改良

対話型UIにする事ができたものの、一つバグがある。
それは、「マック」が略称として判別されないということである。
image.png
「マック」の略称は、私個人の意見としては正しくないと考えているが(思想強め) 公式でも「朝マック」や「マックシェイク 」という名称があるので、略称として正しくなければならない。
(というかマックの名前がついた商品はあるのに、マクドの名前がついた商品ないんだな...)

そこで、マクドナルドの略称を追加、削除するコマンドを追加して対応する。
改良したプログラムがこちら

from cmd import Cmd
from typing import Literal


class MacdonaldCheckShell(Cmd):
    def __init__(self) -> None:
        super().__init__()
        self.macdonald_strings: set[str] = set()

    def do_macdonaldcheck(self, ryakusyou: str) -> None:
        """マクドナルドの略称か判別するコマンド"""
        if ryakusyou in self.macdonald_strings:
            print(f"略称: [{ryakusyou}]は正しい。")
            return

        MACDONALD = "マクドナルド"
        if type(ryakusyou) is not str:
            self.error("文字列ちゃうやんけ")

        elif len(ryakusyou) == 0:
            self.error("空文字やんけ")

        else:
            index = 0
            for i in MACDONALD:
                if ryakusyou[index] == i:
                    index += 1
                    if index == len(ryakusyou):
                        break
            else:
                print(f"略称: [{ryakusyou}]は不正。")
                return
            print(f"略称: [{ryakusyou}]は正しい。")

    def do_macdonaldadd(self, ryakusyou: str) -> None:
        """マクドナルドの略称を追加するコマンド"""
        if ryakusyou in self.macdonald_strings:
            self.error("もう略称になっています")
        else:
            self.macdonald_strings.add(ryakusyou)
            print("追加完了")

    def do_macdonalddelete(self, ryakusyou: str) -> None:
        """マクドナルドの略称を削除するコマンド"""
        if ryakusyou not in self.macdonald_strings:
            self.error("略称に登録されていません")
        else:
            self.macdonald_strings.discard(ryakusyou)
            print("削除完了")

    def do_macdonaldstrings(self, _: str) -> None:
        """マクドナルドの略称に追加された文字を表示するコマンド"""
        if len(self.macdonald_strings) == 0:
            print("マクドナルドの略称は登録されていません")
        else:
            print("マクドナルドの略称")
            for i in self.macdonald_strings:
                print(f"|{i}")

    def do_exit(self, _: str) -> Literal[True]:
        """終了するコマンド"""
        print("exit...")
        return True

    @staticmethod
    def error(*text: str) -> None:
        print("Error: " + "\n".join(text))


if __name__ == "__main__":
    MacdonaldCheckShell().cmdloop()

新たにmacdonaldaddmacdonalddelmacdonaldstringsコマンドを追加
それぞれ追加削除確認ができるコマンドになっている

実際にマックを追加してみると...

image.png
マックを略称と認識することに成功した

さらにいろいろ...

公式ドキュメントを見た感じ、変数を変えたりすることで挙動や見た目を変えることが可能らしい。
(難しく言うと、変数をオーバーライドする。)
ということでいろいろ試してみた

intro

対話型UIに入ったときいれた文字を出力させることができる
マクドナルド判別アプリへようこそ!の文字列を入れてみた
image.png

prompt

入力する場所の左の(Cmd)の部分の文字列を変更することができる
CUIで対話型なので>>>のほうが見やすいかも
image.png

他にもヘルプメッセージ関係でいじれそうなところあるけど、このプログラムにはこの2つで十分かも

完成

from cmd import Cmd
from typing import Literal


class MacdonaldCheckShell(Cmd):
    intro = "マクドナルド判別アプリへようこそ!"
    prompt = ">>> "

    def __init__(self) -> None:
        super().__init__()
        self.macdonald_strings: set[str] = set()

    def do_macdonaldcheck(self, ryakusyou: str) -> None:
        """マクドナルドの略称か判別するコマンド"""
        if ryakusyou in self.macdonald_strings:
            print(f"略称: [{ryakusyou}]は正しい。")
            return

        MACDONALD = "マクドナルド"
        if type(ryakusyou) is not str:
            self.error("文字列ちゃうやんけ")

        elif len(ryakusyou) == 0:
            self.error("空文字やんけ")

        else:
            index = 0
            for i in MACDONALD:
                if ryakusyou[index] == i:
                    index += 1
                    if index == len(ryakusyou):
                        break
            else:
                print(f"略称: [{ryakusyou}]は不正。")
                return
            print(f"略称: [{ryakusyou}]は正しい。")

    def do_macdonaldadd(self, ryakusyou: str) -> None:
        """マクドナルドの略称を追加するコマンド"""
        if ryakusyou in self.macdonald_strings:
            self.error("もう略称になっています")
        else:
            self.macdonald_strings.add(ryakusyou)
            print("追加完了")

    def do_macdonalddelete(self, ryakusyou: str) -> None:
        """マクドナルドの略称を削除するコマンド"""
        if ryakusyou not in self.macdonald_strings:
            self.error("略称に登録されていません")
        else:
            self.macdonald_strings.discard(ryakusyou)
            print("削除完了")

    def do_macdonaldstrings(self, _: str) -> None:
        """マクドナルドの略称に追加された文字を表示するコマンド"""
        if len(self.macdonald_strings) == 0:
            print("マクドナルドの略称は登録されていません")
        else:
            print("マクドナルドの略称")
            for i in self.macdonald_strings:
                print(f"|{i}")

    def do_exit(self, _: str) -> Literal[True]:
        """終了するコマンド"""
        print("exit...")
        return True

    @staticmethod
    def error(*text: str) -> None:
        print("Error: " + "\n".join(text))


if __name__ == "__main__":
    MacdonaldCheckShell().cmdloop()

傍から見てわかりやすくかけるようになっていると思う

実行結果

image.png
ちゃんと動いている

おわり

標準ライブラリにこんな便利なものがあるとは...
他にも便利そうなライブラリがあったので使いたいです

簡単にクラスを使ってアプリを作ることができるので、プログラミング初心者にもおすすめできるかも
クラスの使い方を知れるし、対話型UIの部分を作らなくても良いのがおすすめポイント

あと個人的にオーバーライドは何が良いんだ?と考えていたので驚いた
こういう使い方もあるんだなぁって感じで勉強になった

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?