📑
tkinter/customtkinterでMarkdown記法のファイルを表示するアイデア
記事の要約
pythonのtkinter/customtkinterでMarkdown形式のファイルを読み込みたいとき、それほど高機能でなくてもいい場合は自分でパーサーを書くのが一番シンプル。
あらすじ
- 現在私が趣味で作っているソフトはcustomtkinterでGUIを組んでいて、「使い方」タブがある
- 使い方はソフトの機能追加に合わせて更新したい、かつ多言語対応もしたいのでできれば大本のファイルをMarkdownで記述して、それをcustomtkinterに描画したい
- しかし、Markdownをtkinterに読み込むライブラリは(検索した感じでは)なく、多くはHTMLに一度変換してtkinterに表示する方法を取っている
- 自分でパーサーを書いたらまぁまぁうまくいった
提案
tkinterにMarkdown記法のファイルを読み込む方法は、あらすじで説明しているようにhtmlに一度変換するものがほとんどです。
ですが、これでは余計なライブラリのimportが必要で、またhtmlをtkinterに表示するライブラリは完全なhtmlレンダリングができるものは(当然)ありません。
よって、今回の私のように完全でなくても一部のMarkdown記法の再現ができればよいといった場面であれば、自前のパーサーを書いて表示させるのが最もシンプルであると思います。
tkinterでのサンプルコード
このclassをMarkdown2Tkinter(file_path, target_frame)
として呼び出せばMarkdownファイルが描画されます。
class Markdown2Tkinter:
def __init__(self, path, frame):
self.markdown2Tkinter(path, frame)
def markdown2Tkinter(self, path, frame):
# markdownファイルの読み込み
with open(path, 'r', encoding='utf-8') as f:
text = f.read()
# textを改行で分割
text = text.split('\n')
tkObject = []
# textを1行ずつlineMdToTkinterに渡す
for line in text:
tkObject.append(self.line2Tkinter(line, frame))
# tkObjectの要素を1つずつframeに追加
for i in tkObject:
i.pack(anchor="nw")
def line2Tkinter(self, line, frame):
# 構造化パターンマッチングを使って、textを解析
match StrRe(line):
# lineが#+半角スペースで始まる場合
case "#+ ":
# lineが#で始まる場合、#の数を取得
count = line.count("#")
# lineから#を削除
line = line.replace("#", "")
# lineの前後の空白を削除
line = line.strip()
# labelを作成
label = Label(frame, text=line, font=("Noto Sans CJK JP", 15 + (6 - count) *3), justify="left")
return label
# lineが半角スペース*+*+半角スペースで始まる場合
case "^ *\* ":
# lineが*で始まる場合、*を・に置換
line = line.replace("*", "・")
# labelを作成
label = Label(frame, text=line, font=("Noto Sans CJK JP", 15), justify="left")
return label
case "---":
# lineが---で始まる場合、lineを作成
canvas = Canvas(frame, width=500, height=1, background="gray30", highlightthickness=0)
return canvas
case r"!\[":
# lineが![で始まる場合、()の中身を取得
line = re.findall(r'\((.*?)\)', line)[0].replace(".\\", "")
line = os.path.join(os.path.dirname(__file__), line)
# lineから画像を読み込み
readImage = Image.open(line)
global image
image = ImageTk.PhotoImage(readImage)
# canvasを作成
canvas = Canvas(frame, width=readImage.width, height=readImage.height, highlightthickness=0)
# canvasに画像を描画
canvas.create_image(0, 0, image=image, anchor="nw")
return canvas
case _:
# labelを作成
label = Label(frame, text=line, font=("Noto Sans CJK JP", 15), justify="left")
return label
class StrRe(str):
def __init__(self, var):
self.var = var
pass
def __eq__(self, pattern):
return True if re.search(pattern, self.var) is not None else False
global image
描画結果
今回は下部に記載するMarkdownファイルの表示を目標とすると、今回のサンプルコードを使った場合、この画像の様になります。
また、実際のアプリ制作の際はcustomtkinter等を使うと思いますので、customtkinterで多少見栄えよくしたサンプル画像も載せておきます。
使用したサンプルMarkdownファイル
# TEST用Markdownファイル
テスト用のMarkdownファイルです。
---
* テスト
* テスト
* テスト
![fox](./fox_3d.png)
使用しているライブラリ
- Tkinter
- CustomTkinter(https://github.com/TomSchimansky/CustomTkinter)
- os
- re
画像の出自
参考にしたページ
Discussion