D: tkinter で コンソール widgets を作ってみた
はじめに
はじめまして、D です。
社会からドロップアウトして数ヶ月、現在 Pythonなどなど独学中です。
最近スクレイピングに関心があり、
DarkWeb 用のスクレイピングツールを自作してます。
(Torのインストールから起動までを自動ででき、.onion
にもアクセス可)
そのうち公開するかも...
初投稿で、拙い記事ですが、応援していただけますと幸いです。
記事について
ログが確認できる widget がほしいなと思い作成しました。
その流れで、Python インタラクティブシェルもあったらおもしろいな~と思って
widget として自作しました。
ソース、使い方、ライセンス等は README.md を参照してください。
Log console
が割とツール開発に重宝している。
この記事では、
Python interactive console
の コア部分の解説と
自作 utils
の使い方を紹介していきたいと思います。
ツール開発に役立つかも...
-
紹介する
utils
について-
std_forker
class: 標準出力等を forkするデコレーター (色々利用できるかも) -
Highlight
class:tk.Text
widget 内の文字を ハイライトする (正規表現も対応)
-
-
※
Log console
については、
以下のサイトを参考に作成したので解説は省きます。
本題
実行環境
OS : Widows 10
Python : Python 3.10.8
IDE : PyCharm 2022.2.3 (Community Edition)
インストール
サンプルコードを実行するために、パッケージのインストールをお願いします。
pip install git+https://github.com/BlackHatD/tkinter-console.git
-
アンインストール
pip uninstall tkinter-console -y
Python interpreter console
1. コマンド実行処理について
標準ライブラリである code
を用いて、
Python コマンドを実行しています。
-
code
ライブラリの使用例-
(・ω・)
InteractiveConsole
を用いてコンソールを起動してみる# -*- coding:utf-8 -*- import code # create an instance shell = code.InteractiveConsole(locals()) # start an interactive console shell.interact()
>>> Python 3.10.8 (tags/v3.10.8:aaaf517, Oct 11 2022, 16:50:30) [MSC v.1933 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>
-
(・ω・) 文字列のコードを実行してみる
# -*- coding:utf-8 -*- import code # define a command command = """ for i in range(5): print('Hello World!!') """ # create an instance shell = code.InteractiveConsole(locals()) # compile the string command compiled = code.compile_command(command) # execute the compiled command shell.runcode(compiled)
Hello World!! Hello World!! Hello World!! Hello World!! Hello World!!
コマンドが実行できた!! Σ(゚Д゚)スゲェ!!
上記の例では、標準出力として出力されるため、
tk.Text
widget にinsert
するには
標準出力を変数へ保持する必要が出てくる (´・ω・`)そこで、
utils
のstd_forker
を作成した!! -
-
std_forker
の概要-
sys.stdin
,sys.stdout
,sys.stderr
を最初にバックアップ -
io.TextIOWrapper
インスタンスを作成し、上書き - デコレートされている関数を実行
-
io.TextIOWrapper
オブジェクトのread
メソッドで情報を取得し、
変数へ格納 - 各
std
をリストアする -
callback
関数の第 1引数に、格納した値を渡し、実行
-
std_forker
の使用例-
(・ω・)
help
の出力結果を fork してみる# -*- coding:utf-8 -*- from tkinter_console.utils import std_forker # define a callback function # an argument is required!! def callback(result): print(type(result)) ##print(result) # if use the 'get' method, use a std_forker's class parameter!! # like the below code print(result.get(std_forker.stdout).split()) # define a decorated running function @std_forker(callback=callback) def run(): help("keywords") # run run()
<class 'dict'> ['Here', 'is', 'a', 'list', 'of', 'the', 'Python', 'keywords.', 'Enter', 'any', 'keyword', 'to', 'get', 'more', 'help.', 'False', 'class', 'from', 'or', 'None', 'continue', 'global', 'pass', 'True', 'def', 'if', 'raise', 'and', 'del', 'import', 'return', 'as', 'elif', 'in', 'try', 'assert', 'else', 'is', 'while', 'async', 'except', 'lambda', 'with', 'await', 'finally', 'nonlocal', 'yield', 'break', 'for', 'not']
変数として保持できた!!
-
-
コマンド実行処理と widget 書き込みの流れ
-
callback
を設定したstd_forker
を、実行する関数にデコレート -
std_foker
の__call___
メソッドを実行-
Text
widget に記載された文字コードを コンパイル - コンパイルされたコードを実行
- 標準出力等の情報を
callback 関数
の引数に設定し、実行
-
-
callback 関数
内で別の変数に保存しているため、
その変数を用いてText
widget に書き込む
この utils
のみを利用した 簡易的 一行実行GUI も作ってみた。 (・ω・)
2. ハイライトについて
Python interpreter console
では、
Enter Key
が押されるまで常に入力チェックをしています。
該当箇所 (pyconsole.py)
index を指定して常時ハイライトしており複雑なため、
ここでは、utils
の使い方のみを解説いたします。
-
Highlight
class の使用例- 最低限必要な流れ
- 必要なパッケージを
import
# -*- coding:utf-8 -*- import tkinter as tk from tkinter.scrolledtext import ScrolledText from tkinter_console.utils import Highlight
-
tk.Tk
インスタンスを作成root = tk.Tk()
-
tk.Text
widget のインスタンスを作成 (この例では、ScrolledTextを利用)text = ScrolledText(root) text.pack(fill=tk.BOTH, expand=True)
-
Highlight
インスタンスを作成し、attach
するhighlighter = Highlight() highlighter.attach(text)
- 常時監視する関数を作成する
def run_highlighter(): # inner function def wrapper(): highlighter.do_normal_highlighting() highlighter.do_regex_highlighting() root.after(100, wrapper) root.after(100, wrapper)
- 監視する関数を最初に実行し、
mainloop
させるrun_highlighter() root.mainloop()
- 必要なパッケージを
-
ハイライトの設定
-
do_normal_highlighting
を利用する場合- keyword dict を作成する
例:while
が入力されたとき、 fg をyellow
にするように定義
(もちろん複数定義OK)key_dict = {'while': {'foreground': 'yellow'}}
- 定義を登録する
highlighter.register_normal_tags(key_dict)
- keyword dict を作成する
-
do_regex_highlighting
を利用する場合-
定義を
import
するfrom tkinter_console.utils import PATTERN
-
keyword dict を作成する
例:"
"
で囲まれたとき、 fg をgreen
にするように定義
(もちろん複数定義OK)key_dict = {'double_quotation': { PATTERN: r'".*?"', 'foreground': 'green' }}
-
定義を登録する
highlighter.register_regex_tags(key_dict)
-
-
- Sample
Let's try it!! (´・ω・`)# -*- coding:utf-8 -*- import keyword import builtins import tkinter as tk from tkinter.scrolledtext import ScrolledText from tkinter_console.utils import PATTERN, Highlight if __name__ == '__main__': # define a normal highlight key dict key_normal_dict = {} # use the keyword.kwlist (builtin lib) for key in keyword.kwlist: key_normal_dict[key] = { 'foreground': 'orange' } # use the builtins (builtin lib) key_normal_dict.update({k: {'foreground': 'royalblue'} for k in dir(builtins)}) # define a regex highlight key dict key_regex_dict = { 'double_quotation': { PATTERN: r'".*?"', 'foreground': 'forestgreen' }, 'single_quotation': { PATTERN: r"'.*?'", 'foreground': 'steelblue' }, 'comment': { PATTERN: r"#.*", 'foreground': 'green' } } # create an instance root = tk.Tk() # create a text widget text = ScrolledText(root) text.configure(background='black', foreground='white', insertbackground="silver") text.pack(fill=tk.BOTH, expand=True) text.focus() # create a highlight instance and attach the text widget highlighter = Highlight() highlighter.attach(text) # register the key dicts highlighter.register_normal_tags(key_normal_dict) highlighter.register_regex_tags(key_regex_dict) # create a function def run_highlighter(): # inner function def wrapper(): highlighter.do_normal_highlighting() highlighter.do_regex_highlighting() root.after(100, wrapper) root.after(100, wrapper) # read this file this_file = __file__ with open(this_file, mode='rt', encoding='utf-8') as fd: data = fd.read() text.insert(tk.INSERT, data) run_highlighter() root.mainloop()
以上です。
- 最低限必要な流れ
終わりに
ツール開発している人の助けになれたらなと思ってます(・ω・)
早く人間になりたい...
Discussion