こんにちは。
この記事は↓の続きです。
smooth-pudding.hatenablog.com
キーの設定が面倒くさい
前回の記事で、キーを割り当ててフットスイッチを使えるようになりました。ただ、設定するには
- footswitch のバイナリのあるディレクトリに移動して
- 特定のコマンドを打って
- 管理者パスワードを打つ
が必要で非常に面倒くさい。なんとかしようと思いました。
そもそもの話、設定したいキーバインドなんて数通りしかありません。せいぜいターミナルを起動するかプログラムを実行するかぐらいでしょう。であれば、設定のリストを表示してそこから選んで設定できたら楽じゃね?と考えました。
Python 便利
で、Python で作りました:
使い方は簡単、起動して設定したいラジオボタンを選択して、管理者権限のパスワードを入力してOK押すだけです。一回入力すればその後はパスワード入力も不要です。ちなみにこのアプリは ctrl+shift+alt+F で起動するようにしました。なんと便利なんでしょう。
ポイントはこんな感じです↓
- footswitch を設定する実行ファイルを sudo で見える場所に置く(私は /usr/bin に起きました)
- python コードを実行可能にして作る(#!/usr/bin/python3 を一行目に設定して chmod 755 する)
- ショートカットキーに↑の python ファイルを割り当てる
- 触りやすいところにコードを配置しておき、横にパスの通った場所にコピーするシェルスクリプトをおいておく
せっかくなのでひとつひとつ記録に残しておきます。
footswitch を設定する実行ファイルを sudo で見える場所に置く
これはそのまんまです。前記事で紹介した github のコードをビルドしてできるコードを /usr/bin にコピーするだけです
$ cp footswitch /usr/bin/
python コードを実行可能にして作る
Python コード自体はこの記事の最後に載せますが、一行目にそのスクリプトを実行するインタプリタのパスを書いておいて権限を適当に定めると、単にそのファイルを叩くだけで実行できるようになります。私の環境だと /usr/bin/python3 で実行できるように #!/usr/bin/python3 を一行目に足しました。あと chmod コマンドを使って権限を書き換えました:
$ chmod 755 footswitch_gui # .py はあってもいいけど気分で除去
こちらもパスが通っている場所におけばいいのですが、こちらの起動は sudo 権限がなくて良いので、/home/pudding/.local/bin においておくことにしました:
$ cp footswitch_gui /home/pudding/.local/bin/
ショートカットキーに↑の python ファイルを割り当てる
Ubuntu だと「設定 > キーボードショートカット」から自分でショートカットキーを割り当てることができます。一番下の + をクリックすると下のような画面が出てきます:
名前は適当に、コマンドは footswitch_gui を設定し、「ショートカットを設定」から適当なキーバインドを割り当てます。私は絶対にかぶらなさそうな ctrl+shift+alt+F にしました。
触りやすいところにコードを配置しておき、横にパスの通った場所にコピーするシェルスクリプトをおいておく
私の PC では大体同じところに python ファイルをおいているので、footswitch の gui のスクリプトも同じところにおいておきたいという気持ちがあります。それに対応するために、超単純な↓のスクリプト(install_gui.sh)を書きました:
#!/usr/bin/sh cp footswitch_gui.py ~/.local/bin/footswitch_gui
ソースコード
最後に python のソースコードをおいておきます。似たツールを作りたい方の参考になれば。
#!/usr/bin/python3 from enum import Enum import subprocess import tkinter as tk from tkinter import messagebox import tkinter.ttk as ttk class NoPasswordError(Exception): pass class Arg(Enum): # @formatter:off DEFAULT = ("Do Nothing" , "" ) OPEN_TERMINAL = ("Open Terminal" , "-m ctrl -m alt -k t" ) PYCHARM_DEBUG = ("PyCharm Debug" , "-m shift -m alt -k F9" ) PYCHARM_EXECUTE = ("PyCharm Run" , "-m shift -m alt -k F10") VSCODE_BUILD = ("VSCode Build" , "-m ctrl -m shift -k b" ) OVERLEAF_COMPILE = ("Overleaf Compile", "-m ctrl -k s" ) # @formatter:on def __init__(self, alias, command): self.alias = alias self.command = command @classmethod def from_string(cls, string: str) -> "Arg": for arg in Arg: if arg.alias == string: return arg raise ValueError(f"no argument found with alias {string}") class MainWindow(ttk.Frame): def __init__(self, master): super().__init__(master) self.pack(fill=tk.BOTH, expand=True) self.create_variable() self.config_master() self.create_widget() def create_variable(self): self.strvar_radio = tk.StringVar() self.strvar_radio.set(Arg.DEFAULT.alias) self.strvar_passwd = tk.StringVar() def config_master(self): self.master.title("FootSwitchManager") self.master.geometry("270x270") self.master.resizable(height=False, width=False) self.master.bind("<Return>", lambda e: self.click_button_ok()) self.master.protocol("WM_DELETE_WINDOW", self.click_x) def create_widget(self): for arg in Arg: tk.Radiobutton( self, text=arg.alias, value=arg.alias, variable=self.strvar_radio, anchor=tk.W, ).pack(expand=True, fill=tk.BOTH) tk.Label(text="< Password >").pack(expand=True, fill=tk.BOTH) self.entry_passwd = tk.Entry( show="*", textvariable=self.strvar_passwd, ) self.entry_passwd.pack(expand=True, fill=tk.BOTH, padx=5) button_ok = tk.Button(text="OK", command=self.click_button_ok) button_ok.pack(expand=True, fill=tk.BOTH, padx=20, pady=5) def click_x(self): ok_to_quit = messagebox.askyesno( title="Quit", message="Quit?", ) if ok_to_quit: self.master.quit() self.master.destroy() def click_button_ok(self): try: arg = Arg.from_string(self.strvar_radio.get()) passwd_str = self.strvar_passwd.get() if not passwd_str: raise NoPasswordError passwd_bytes = (passwd_str + "\n").encode() self.execute_command(arg, passwd_bytes) except NoPasswordError: messagebox.showerror( title="Error", message="Please input the admin password.", ) return except subprocess.CalledProcessError: messagebox.showerror( title="Error", message="The password you typed is wrong." ) return except Exception as e: messagebox.showerror( title="Error", message=f"Some unexpected error occurred:\n{e}", ) return else: messagebox.showinfo( title="Information", message="Successfully configured the foot switch.", ) self.entry_passwd.configure(state="disabled") @staticmethod def execute_command(arg: Arg, passwd: bytes): command = f"sudo -S footswitch -2 {arg.command}" subprocess.run(command.split(), input=passwd, check=True) if __name__ == "__main__": app = MainWindow(tk.Tk()) app.mainloop()