【完成版】Pythonのtkinterで辞書の語義を並列表示するGUIアプリを制作してみた【Docker & Jupyterlab環境】

はじめに

tkinterで初めてのGUIアプリ開発を行う。

制作したいのは、アメリカ版とイギリス版を有するとある英語辞書サイトから調べたい単語の語義をスクレイピングして並列表示させるインターフェイス。

Pythonは様々なモジュールがあるので色々なことが出来るだろうし、実際それに助けられた訳だが、実装するのに難儀した意外なコトも存在した。
それの解決方法は英語サイトにあって、それを見つけて試してテストして思い通りの結果になった時のアレときたら(酒飲むべきだろうが何せ下戸なもので)。

ということで今回は、完成できた喜びを噛み締めつつ、難儀したコトの実装方法の概説をしていくことにする。

コードの各詳説は別エントリにて行う予定。



開発環境

Python、およびそのモジュールは、Docker内に環境を整備し、Jupyterlabで操作していく。

  • マシン:MacBook Pro
  • OS:macOS Big Sur(11.6.3)
  • 仮想環境:Docker Desktop(4.4.2 (73305))
  • エディタ:JupyterLab(3.2.9)、Sublime Text(Build 4126)
  • XQuartz:2.8.1
  • python:3.8.10
  • tkinter:8.6
  • requests:2.27.1
  • beautifulsoup4:4.10.0


階層構造

ファイルのディレクトリは以下の通りに予め設定。

desktop
  └python_webscrapingフォルダ
       ├Docker Composeファイル
       ├Dockerfile
       └workフォルダ
          └pythonファイル(jupyterlabで作成・編集)


Xquartzの導入と設定

上記に追加で、予めXquartzの導入と設定が必要になります。
導入および設定の方法は、下記エントリの『1 MacにXQuartzをインストール』および『2 XQuartzの環境設定を開く』をご覧あれ。

MacでPythonのGUIライブラリをDocker Container内のJupyterlabから稼働させる方法

DockerのContainerに構築したPython環境ではJupyterlabからTkinterが動きませんが、ある方法で動かすことが出来るようになります。その方法を丁寧に分かりやすく解説します。



Docker Desktopをインストール

Docker Desktopをインストールしていない場合は、下記ブログの1と2を参考にしていただければ。

MacにDockerをインストールしPythonが使えるまでのまとめ【初心者向け】

初心者がDockerをインストールするのはハードルが高いですよね。初心者である筆者がDockerのインストール方法を調べて試してみたのでその手順を公開します。用語については、理解しないままとりあえず放置します。理解しようと用語を調べだしたらそれに時間を取られてインストールに進めないので。



Dockerfileの内容

FROM ubuntu:latest

LABEL version="1.0" \
      maintainer="eszett design" \
      description="Webスクレイピング用"

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update && apt-get install -y \
		python3 \
		python3-pip \
		libx11-dev \
		python3-tk

RUN pip3 install jupyterlab \
		requests \
		beautifulsoup4 


Docker Composeの内容

version: "3.8"
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - DISPLAY=host.docker.internal:0.0
    volumes:
      - ./work:/work
    ports:
      - 8030:8888
    image: python_webscr:latest
    container_name: python_webscr
    command:
      jupyter lab
        --ip=0.0.0.0
        --allow-root
        --notebook-dir='/work'
        --LabApp.token=''


ImageとContainerの作成

以下のエントリに解説しているのでご覧あれ。

ImageとContainerの作成|【完成版】MacでJupyterlabからPythonのGUIライブラリが使えるDockerの設定方法

PythonのGUIライブラリ(Tkinter、pillow、openpyxl)をDocker Container内にインストールし、JupyterlabからXQuartzを介して稼働させる方法と、Docker ComposeとDockrfileの作成方法を紹介します。



完成したコード

# 標準モジュール
import tkinter as tk
from tkinter import ttk
import tkinter.font as font
import webbrowser
import re
import threading as th
# インストール必要なモジュール
import requests
from bs4 import BeautifulSoup

# ---命名規則---
# 変数などの名は、各単語の頭文字を大文字に。
# 熟語は結合する(例:Text Box→TextBox)
# 熟語において、前の単語語尾が大文字または小文字のb, d, f, h, k, lの時は、単語間を「_」で繋ぐ(判読性、可読性優先)(例:Word Means→Word_Means(WordMeansは読み辛い)
# その他、判読性、可読性優先のための例外を認める


# 単語の意味取得定義
class Dictionary:
    def __init__(self, Nation, URL, TextBox):
        self.Nation = Nation # US or UK
        self.URL = URL # 調べたい単語のURL
        self.TextBox = TextBox # 意味表示用TextBoxインスタンス

    # 単語の意味取得(HTMLタグ付き)
    def GetMeans(self):
        self.Means = []
        self.HTML = requests.get(self.URL)
        self.Soup = BeautifulSoup(self.HTML.content, "html.parser")
        for Classes in self.Soup.select("section.gramb"):
            self.Word_Classes = Classes.find("h3", class_ = "pos")
            self.Means.append(self.Word_Classes)
            for Word_Means in Classes.select("h3 + .transitivity, h3 + .variant, .gramb > .grammatical_note, .trg .iteration, .trg .variant, .trg .sense-regions, h3 ~ .sense-regions, .trg .sense-registers, h3 ~ .sense-registers, .trg .crossReference, h3 ~ .empty_sense > .crossReference, .trg .form-groups, .trg p .grammatical_note, .trg .iteration ~ .ind, .subSense > span"):
                self.Means.append(Word_Means)
        return self.Means
    
    # 単語の意味テキスト整形
    def RegExs(self):
        self.GetMeans = self.GetMeans() # リスト型
        self.Mapped = map(str, self.GetMeans)
        self.MeanText = "".join(self.Mapped) # 文字列型

        # 2つ以上のスペースを1つに
        self.Pattern0 = re.compile(r"\s{2,}", re.MULTILINE | re.DOTALL)
        self.RegText = re.sub(self.Pattern0, " ", self.MeanText)
        # 改行する箇所1:品詞の直後
        self.Pattern1 = re.compile(r"(||||
)", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern1, "\n\\1", self.RegText) # 改行する箇所2:2番目以降の品詞の直前 self.Pattern2 = re.compile(r"(?", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern2, "\n\n", self.RegText) # 改行する箇所3:TRANSITIVE VERB直後の[WITH OBJECT]直後 self.Pattern3 = re.compile(r"(\[)(.*?)(\])", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern3, "\\1\\2\\3\n", self.RegText) # 改行する箇所4:意味の直後、詳細意味の直前 self.Pattern4 = re.compile(r"()", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern4, "\n\\1", self.RegText) # 空の大意味番号タグ削除 self.Pattern5 = re.compile(r"", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern5, "", self.RegText) # 大意味番号「1」のタグ削除 self.Pattern6 = re.compile(r"(1)", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern6, "\\1 ", self.RegText) # 大意味番号「1」以外のタグ削除と改行 self.Pattern7 = re.compile(r"(\d{1,})", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern7, "\n\\1 ", self.RegText) # 小意味番号のタグ削除 self.Pattern8 = re.compile(r"(\d{1,}\.\d{1,})", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern8, "\\1 ", self.RegText) # 括弧で囲む1:grammatical_note グループ番号は、"\\1", "\g<1>", r"\1"がある。 self.Pattern9 = re.compile(r"(.*?)", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern9, "«\\1» ", self.RegText) # 括弧で囲む2:form-groups self.Pattern10 = re.compile(r"((.*?)(.*?))*?", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern10, "«\\2\\3» ", self.RegText) # 括弧で囲む2:sense-regions self.Pattern11 = re.compile(r"([\w ]+) ", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern11, "«\\1» ", self.RegText) # 括弧で囲む3:sense-registers self.Pattern12 = re.compile(r" (.*?) ", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern12, "«\\1» ", self.RegText) # variant1 self.Pattern13 = re.compile(r"
(\(also )(.*?)((, )(.*?))*?(\))
", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern13, "\\1\\2\\4\\5\\6 ", self.RegText) # variant2 self.Pattern14 = re.compile(r"
(\()(.*?) (.*?)((, )(.*?))*?(\))
", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern14, "\\1\\2 \\3\\5\\6\\7 ", self.RegText) # crossReference self.Pattern15 = re.compile(r"
(.*?)(<.*?>(.*?)<.*?>)*?
", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern15, "\\1\\3", self.RegText) # 語義 self.Pattern16 = re.compile(r"(.*?)", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern16, "\\1", self.RegText) # 品詞右の変化表削除 self.Pattern17 = re.compile(r"(.*?)", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern17, "", self.RegText) # 最後に残りのタグを全消去 self.Pattern18 = re.compile(r"<.*?>", re.MULTILINE | re.DOTALL) self.RegText = re.sub(self.Pattern18, "", self.RegText) # 品詞の選択 self.Pattern = re.compile(r"^([a-z\s]+?)(?=$)", re.MULTILINE | re.DOTALL) # 品詞を大文字に self.RegText = re.sub(self.Pattern, lambda ClassWord: ClassWord.group(0).upper(), self.RegText) return self.RegText # TextBox内組版定義 class Typogaphy(tk.Text): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) def RegExp_Match(self, TextBox, Pattern, Tag, Start = "1.0", End = "end", Regexp = True): Start = TextBox.index(Start) End = TextBox.index(End) TextBox.mark_set("matchStart", Start) TextBox.mark_set("matchEnd", Start) TextBox.mark_set("searchLimit", End) Count = tk.IntVar() while True: Index = TextBox.search(Pattern, "matchEnd", "searchLimit", count = Count, regexp = Regexp) if Index == "": break if Count.get() == 0: break TextBox.mark_set("matchStart", Index) TextBox.mark_set("matchEnd", "%s+%sc" % (Index, Count.get())) TextBox.tag_add(Tag, "matchStart", "matchEnd") # Frame定義 class Frames(tk.Frame): def __init__(self, Master, Typeface, LightColor, DarkColor, WhiteColor, MiddleColor): super().__init__(Master) self.Typeface = Typeface # Typeface ≒ Font self.LightColor = LightColor # foreground = light gray self.DarkColor = DarkColor # background = dark gray self.WhiteColor = WhiteColor # background = white self.MiddleColor = MiddleColor # 国名ボタン activebackground = middle gray # 画面サイズ Master.geometry("1920x1176") # 画面タイトル Master.title("Search the Word | Lexico.com powered by Oxford") # TopFrame定義 self.TopFrame = tk.Frame(Master, bg = self.LightColor) self.TopFrame.place(x = 0, y = 0, width = 1920, height = 231) # 見出し self.Head_Font = font.Font(Master, family = Typeface, size = 20, weight = "bold") self.Head_Label = tk.Label(self.TopFrame, text = "Oxford Dictionaries", font = self.Head_Font, fg = self.LightColor, bg = self.DarkColor) self.Head_Label.place(x = 0, y = 0, width = 1920, height = 84) # Wordラベル self.Word_Font = font.Font(Master, family = Typeface, size = 12) self.Word_Label = tk.Label(self.TopFrame, text = "Word:", font = self.Word_Font, anchor = "e", padx = 5, bg = self.LightColor) self.Word_Label.place(x = 360, y = 105, width = 240, height = 42) # 単語入力用テキストボックス self.EntryWord = tk.StringVar() self.EntryFont = font.Font(Master, family = Typeface, size = 14, weight = "bold") self.EntryBox = tk.Entry(self.TopFrame, textvariable = self.EntryWord, font = self.EntryFont) self.EntryBox.focus_set() self.EntryBox.place(x = 600, y = 105, width = 720, height = 42) # Deleteボタン self.Del_Button = tk.Button(self.TopFrame, text = "Delete") self.Del_Button.place(x = 1320, y = 105, width = 240, height = 42) # Findボタン self.Find_Font = font.Font(Master, family = Typeface, size = 12) self.Find_Button = tk.Button(self.TopFrame, text = "Find", font = self.Find_Font) self.Find_Button.place(x = 600, y = 168, width = 360, height = 42) # Clearボタン self.ClearButton = tk.Button(self.TopFrame, text = "Clear") self.ClearButton.place(x = 960, y = 168, width = 360, height = 42) # LeftFrame定義 self.LeftFrame = tk.Frame(Master, bg = self.LightColor) self.LeftFrame.place(x = 0, y = 231, width = 960, height = 924) # USボタン self.US_Font = font.Font(Master, family = Typeface, size = 12, weight = "bold") self.US_Button = tk.Button(self.LeftFrame, text = "US", anchor = "center", font = self.US_Font, fg = self.LightColor, bg = self.DarkColor, activebackground = self.MiddleColor) self.US_Button.place(x = 120, y = 0, width = 732, height = 42) # 意味出力用テキストボックス self.LeftFont = font.Font(self.LeftFrame, family = Typeface, size = 11) self.LeftTextBox = tk.Text(self.LeftFrame, bg = self.WhiteColor, relief = "sunken", borderwidth = 1, padx = 10, pady = 10, wrap = "word", spacing2 = "4p", spacing3 = "12p", font = self.LeftFont) self.LeftTextBox.place(x = 120, y = 42, width = 720, height = 840) # 縦方向スクロールバーの作成 self.yScroll = tk.Scrollbar(self.LeftFrame, orient = "vertical", command = self.LeftTextBox.yview) self.yScroll.place(x = 840, y = 42, height = 840) # 横方向スクロールバーの作成 self.xScroll = tk.Scrollbar(self.LeftFrame, orient = "horizontal", command = self.LeftTextBox.xview) self.xScroll.place(x = 120, y = 882, width = 720) # 動きをスクロールバーに反映 self.LeftTextBox["yscrollcommand"] = self.yScroll.set self.LeftTextBox["xscrollcommand"] = self.xScroll.set # RightFrame定義 self.RightFrame = tk.Frame(Master, bg = self.LightColor) self.RightFrame.place(x = 960, y = 231, width = 960, height = 924) # UKボタン self.UK_Font = font.Font(Master, family = Typeface, size = 12, weight = "bold") self.UK_Button = tk.Button(self.RightFrame, text = "UK", anchor = "center", font = self.UK_Font, fg = self.LightColor, bg = self.DarkColor, activebackground = self.MiddleColor) self.UK_Button.place(x = 120, y = 0, width = 732, height = 42) # 意味出力用テキストボックス self.RightFont = font.Font(self.RightFrame, family = Typeface, size = 11) self.RightTextBox = tk.Text(self.RightFrame, bg = self.WhiteColor, relief = "sunken", borderwidth = 1, padx = 10, pady = 10, wrap = "word", spacing2 = "4p", spacing3 = "12p", font = self.RightFont) self.RightTextBox.place(x = 120, y = 42, width = 720, height = 840) # 縦方向スクロールバーの作成 self.yScroll = tk.Scrollbar(self.RightFrame, orient = "vertical", command = self.RightTextBox.yview) self.yScroll.place(x = 840, y = 42, height = 840) # 横方向スクロールバーの作成 self.xScroll = tk.Scrollbar(self.RightFrame, orient = "horizontal", command = self.RightTextBox.xview) self.xScroll.place(x = 120, y = 882, width = 720) # 動きをスクロールバーに反映 self.RightTextBox["yscrollcommand"] = self.yScroll.set self.RightTextBox["xscrollcommand"] = self.xScroll.set def main(): # Tkクラス生成 Root = tk.Tk() # フォント設定 Face = "Yu Gothic UI" # 色設定 White = "#fff" # 白 White LightGray = "#efefef" # 淡灰色 Light Gray Dark_Gray = "#333" # 濃灰色 Dark Gray Middle_Gray = "#ddd" # 灰色 Gray # フレームインスタンス Fs = Frames(Master = Root, Typeface = Face, LightColor = LightGray, DarkColor = Dark_Gray, WhiteColor = White, MiddleColor = Middle_Gray) Typ = Typogaphy() # 将来的にテキストの見映をここで行うように引数を設定する予定 # 意味の出力 def Find_the_Mean(*args): global Link_US, Link_UK, Dic_Info # 各辞書のURL Link_US = "https://www.lexico.com/en/definition/" + Fs.EntryWord.get() Link_UK = "https://www.lexico.com/definition/" + Fs.EntryWord.get() # 各辞書の諸情報(国名、各辞書単語のURL、表示するTextBox) Dic_US = Dictionary(Fs.US_Button["text"], Link_US, Fs.LeftTextBox) Dic_UK = Dictionary(Fs.UK_Button["text"], Link_UK, Fs.RightTextBox) Dic_Info = [Dic_US, Dic_UK] # 各辞書から意味を取得 for Dic in Dic_Info: RegTexts = Dic.RegExs() # 意味をテキストボックスに挿入(表示) Dic.TextBox.insert(1.0, RegTexts) # 品詞の色付け Dic.TextBox.tag_configure("Word_Class", foreground = "#FF4500") Typ.RegExp_Match(Dic.TextBox, "^[A-Z\s]+?$", "Word_Class") # Senseをインデント Dic.TextBox.tag_configure("Sense_Indent", foreground = "#111", lmargin1 = "0p", lmargin2 = "12p") Typ.RegExp_Match(Dic.TextBox, "^\d{1,2}\s.+$", "Sense_Indent") # Subsenseをインデント Dic.TextBox.tag_configure("Subsense_Indent", foreground = "#545454", lmargin1 = "20p", lmargin2 = "44p") Typ.RegExp_Match(Dic.TextBox, "^\d{1,2}\.\d{1,2}.+$", "Subsense_Indent") # « »と[ ]を緑色に Dic.TextBox.tag_configure("Green", foreground = "#008000") Typ.RegExp_Match(Dic.TextBox, "[«\[].+?[»\]]", "Green") # Findボタンクリック 意味を表示 def Click_Find_Button(*args): Thread = th.Thread(target = Find_the_Mean) Thread.start() # Clearボタンクリック 意味を全削除 def Clear_the_TextBoxes(*args): for Dic in Dic_Info: Dic.TextBox.delete(1.0, "end") # Deletボタンクリック 入力単語を削除 def Delete_the_Word(*args): Fs.EntryBox.delete(0, "end") # 「US」または「UK」ボタンを左クリック時、調べた単語の頁を開く def Open_the_URL(URL): webbrowser.open_new(URL) # Returnキーを押した時<Find> Fs.EntryBox.bind("", Click_Find_Button) # Shift+Returnキーを押した時<Clear> Root.bind("", Clear_the_TextBoxes) # Findボタンクリック時のコマンド指定 Fs.Find_Button["command"] = Click_Find_Button # Clearボタンクリック時のコマンド指定 Fs.ClearButton["command"] = Clear_the_TextBoxes # Deletボタンクリック時のコマンド指定 Fs.Del_Button["command"] = Delete_the_Word # 「US」ラベルを左クリック時、調べた単語の頁を開く Fs.US_Button.bind("", lambda e: Open_the_URL(Link_US)) # 「UK」ラベルを左クリック時、調べた単語の頁を開く Fs.UK_Button.bind("", lambda e: Open_the_URL(Link_UK)) Fs.mainloop() if __name__ == "__main__": main()


Jupyterlabを開く

Container作成後、ブラウザを開き、URL欄にlocalhost:8030と入力すると、Jupyterlabが開くので、それに上記コードを入力してShift+ReturnでGUIアプリが開く
詳細は、以下をご覧あれ。

Jupyterlabをブラウザで開きtkinterを稼働させる|【完成版】MacでJupyterlabからPythonのGUIライブラリが使えるDockerの設定方法<

PythonのGUIライブラリ(Tkinter、pillow、openpyxl)をDocker Container内にインストールし、JupyterlabからXQuartzを介して稼働させる方法と、Docker ComposeとDockrfileの作成方法を紹介します。



実現したかったこと

  • tkinterでGUIアプリ開発
  • WEBスクレイピング
  • 自作の組版エンジン実装による、読みやすいテキストの制御と表示

GUIアプリのレイアウト方法

フレームを3つ用意し、各々の中に各パーツを、Placeにて配置している。


完成版のウインドウ。上と左右の3フレームを用意、中に各種パーツを配置

Frameとパーツの配置方法は、以下のエントリをご覧あれ。

【pack/grid/placeの使い分け】Pythonのtkinterで制作のGUIアプリでの部品のレイアウト方法【Docker & Jupyterlab環境】

tkinterで制作のGUIアプリでの部品のレイアウトでplaceを使った方法を紹介する。フレームを複数配置した場合は、部品の配置座標に注意が必要。


WEBスクレイピングの方法

以下のサイトを読み込み、実装した。
長いが丁寧に解説されている。

図解!PythonでWEB スクレイピングを極めよう!(サンプルコード付きチュートリアル) - AI-interのPython3入門

Python3におけるWEBスクレイピングのやり方について初心者向けに解説した記事です。
Requests、Beautiful Soup、Selenium、Pandas、newspaper3kなどの基本的なライブラリの使い方を、サンプルコード付きのチュートリアル形式で、具体的な例を用いて解説していきます。



自作組版エンジン実装に難儀

以下を実装しようと奮起。

  • 狙った箇所に改行と括弧を追加
  • 行の折返しの行頭を揃える
  • サブの語義を字下げする
  • 品詞名を大文字に
  • 狙った箇所の色付け

一手間二手間かけてトライ&エラーを繰り返し、ネット検索も日本語だけでなく英語のページまで調査しまくり実現に漕ぎ着けた。
結局のところ、2週間で8回改良した。以下で、解説する。


狙った箇所に改行と括弧を追加

当初、WEBスクレイピングでのテキスト取得はタグなしを指定していた。

取得したタグなしテキスト(リスト型)を、

"\n".join()

で改行を付けた文字列型に変換して表示していた。

上図のように、1.1のような形式の番号も改行されてしまっている。逆に整数のみの番号は改行されず語義との間にスペースもない。

これだと、後付でテキスト整形が出来ない。

その後試行錯誤し、最終的にクラス名を細かく指定して必要なテキストを取得するようにし、かつ後付でテキスト整形できるよう、HTMLタグ付きにした。

改行したい箇所や括弧で囲みたい語のあるHTMLタグとクラス名を見極め、それを正規表現でのターゲットにしてテキスト整形していった。


取得したHTMLタグ付きテキスト

正規表現でのターゲット設定後に単語検索して結果を確認すると、新たに設定すべき箇所が出てきて、それをまた正規表現で設定を追加して…、を繰り返し、狙うべきHTMLタグを設定すること、実に18回分
何とか完成に漕ぎ着けた。


(左)最初の取得結果 (右)タグ付きで取得後正規表現で整形後
右では改行すべきところで改行され、括弧も付けられている。

この作業で気付いたことに、Pythonでの正規表現の記法は、自分の知っている記法と少しだけ異なった。
グループ番号の表現が、自分の知っている記法では「$1」とドルマークと数字だったが、Pythonでは「\\1」「\g<1>」「r"\1"」とのことで、最初間違えた。

詳しくは、上に掲載したコードで定義したRegExs関数内をご覧あれ。

pythonの正規表現については、公式リファレンスをご覧あれ。

re --- 正規表現操作 — Python 3.8.10 ドキュメント

このモジュールは Perl に見られる正規表現マッチング操作と同様のものを提供します。


行の折返しの行頭を揃える/サブの語義を字下げする

調べた結果、Text Boxに表示されるテキストの特定箇所にタグを追加し、そのタグにスタイルを指定することで、テキスト整形できる。

が、正規表現を使用しての特定箇所の指定方法が長らく不明だった。

更に調べた結果、その方法を見つけた。以下のサイトのコードがそれ。これを見つけた時、小躍りしてしまった。

使用する際は、regexp=FalseをTrueにすると、patternで正規表現が使用できる。

実際の使用では、tag_configureでインデント用オプションのlmargin1lmargin2を設定し、.RegExp_Match関数(参考サイトでのhighlight_pattern関数名を変更)にて正規表現でターゲットを絞り、tag_configureの設定をタグ付けしている。
(ついでに文字色も設定している)

# Senseをインデント
Dic.TextBox.tag_configure("Sense_Indent", foreground = "#111", lmargin1 = "0p", lmargin2 = "12p")
Typ.RegExp_Match(Dic.TextBox, "^\d{1,2}\s.+$", "Sense_Indent")

# Subsenseをインデント
Dic.TextBox.tag_configure("Subsense_Indent", foreground = "#545454", lmargin1 = "20p", lmargin2 = "44p")
Typ.RegExp_Match(Dic.TextBox, "^\d{1,2}\.\d{1,2}.+$", "Subsense_Indent")

(左)インデント処理する前 (右)インデント処理後

詳細は、以下のサイトをご覧あれ。

tkinterテキストウィジェットでテキストを強調表示する方法 - CODE Q&A

特定のパターンに基づいて特定の単語や表現のスタイルを変更する方法を知りたいです。 Tkinter.Text ウィジェッ...


狙った箇所の色付け/品詞名を大文字に

前項と同じやり方で、tag_configureで文字色のオプションforegroundで色を設定し、Text Boxに表示されるテキストの特定箇所にタグを追加すれば、色を付けることが出来る。

# 品詞の色付け   
Dic.TextBox.tag_configure("Word_Class", foreground = "#FF4500")
Typ.RegExp_Match(Dic.TextBox, "^[A-Z\s]+?$", "Word_Class")

# «  »と[  ]を緑色に
Dic.TextBox.tag_configure("Green", foreground = "#008000")
Typ.RegExp_Match(Dic.TextBox, "[«\[].+?[»\]]", "Green")

品詞を大文字に置換する方法は、下のサイトを参考にコードを作成した。

# 品詞を大文字に(RegTextsは取得した語義テキスト)
RegTexts = re.sub("^([a-z\s]+?)(?=$)", lambda ClassWord: ClassWord.group().upper(), RegTexts)

が、全く変化なし。なぜ?

色々試した結果、どうも行頭^行末$付きの正規表現はこの式では適用されないようで、これが最後の難儀だったが、試しにcompileしてみたらあっさり解決

# 品詞の選択(正規表現をコンパイル)
Pattern = re.compile(r"^([a-z\s]+?)(?=$)", re.MULTILINE | re.DOTALL)
# 品詞を大文字に
RegTexts = re.sub(Pattern, lambda ClassWord: ClassWord.group().upper(), RegTexts)

これで品詞が大文字になった(ついでに文字色も変更)。


(左)色変更前 (右)色変更後

実際の稼働している動画も掲載しておく。


品詞名を大文字に1

How to Convert a String to Title Case Correctly in Python

In this tutorial, you'll learn how to convert a string to title case in Python using the title() method or a helper function titlecase().


品詞名を大文字に2

Python で文字列の最初の文字を大文字にする

文字列の最初の文字を大文字化するには、capitalize()、title()、capwords()、regex、string、ユーザー定義のメソッドを使用することができます。



さいごに

最終的に、正規表現で加工する工程と、取得した語義を加工する工程のコードが非常に長くなってClassや関数が煩雑になったけども、とりあえず放置。
最後、急遽スレッド処理をFindボタンにセットして処理を少し軽くしてみた。スレッド処理については、Classでスレッドを継承できるらしいので、スレッド処理するものをまとめるのも良いかも。
そのへんは今後python学習していく中で徐々に整理していければ。
現在は完成させることが大事。



参照元

今回は非常に多くのサイト・ブログにお世話になった。
以下に、上記で挙げた以外の参考サイト・ブログをまとめて掲載する。



tkinterを使ったGUIアプリの作成方法



ドロップダウンリストの作り方



ドロップダウンボックスの作り方



Classの使い方とまとめ方(コメント欄の方を参照)

【Python】Tkinterによる200行で作るGUIアプリ「英単語ソフト」 - Qiita

今回はPythonの勉強と英語の勉強を兼ねて、「英単語ソフト」を作ってみようと思います。GUIで作成したいので、tkinterを使って実装していきます。



Classの作り方

python tkinterのクラス化手法によるGUI作成 - memopy

python tkinterのドキュメント"A Simple Hello World Program"が難しすぎる 皆さんはpython tkinterのドキュメントをご覧になったことがあるだろうか。 ※python3の場合 25.1. tkinter — Python interface to Tcl/Tk — Python 3.6.1 documentation ※python2の場合 24.1. Tkinter — Python interface to Tcl/Tk — Python 2.7.13 documentation A Simple Hello World Program こ…



Classの作り方

Pythonのリストでクラスのオブジェクトを格納する - Qiita

Python のリストにクラスのオブジェクトを入れる
Pythonのリストには,クラスのオブジェクトを入れることができます.



スクロールバーを設置する方法

Tkinterの使い方:スクロールバー(Scrollbar)の使い方

このページでは、Tkinter の「スクロールバー」ウィジェットの作成方法および設定方法について説明していきたいと思います。 スポンサーリンク Contentsスクロールバーとはスクロールバーは表示領域を変化させるウィジ ...



TextBoxにスクロールバーを設置する方法

【Python GUI】スクロールバーを作る方法【Tkinter】 | 転職だらけ

PythonのTkinterを使ってGUIアプリケーションを作る際、スクロールバーの作成方法・導入方法について詳しく解説していきます。縦方向だけでなく横方向のスクロールバーについても書いていきます。



Frameの使い方

Tkinterの使い方:フレームウィジェット(Frame)の使い方

このページでは、Tkinter における「フレーム」ウィジェットの使い方やフレームを利用するメリット、設定方法について説明します。 スポンサーリンク Contentsフレームとはフレームウィジェットの作り方・使い方メイン ...



ウィジェットの配置方法

Tkinterの使い方:ウィジェットの配置(pack・grid・place)

このページでは Tkinter でのウィジェットの配置について解説します。 Tkinter では「ウィジェットを作成」し、そのウィジェットを親ウィジェットの中(例えばメインウィンドウの中)に「配置」することで、GUI ア ...



Entryボックスがアクティブに

テキストボックスのフォーカスを切り替え【tkinter】 – なゆたり

Python の GUI ライブラリである tkinter では、テキストボックスが入力対象(アクティブになっている)かを『フォーカス』という言葉で表現します。 このフォーカスを...



Beautiful Soup

[Python入門]Beautiful Soup 4によるスクレイピングの基礎

Beautiful Soup 4を使って、urllib.request.urlopen関数などで取得したHTMLファイルから情報を抜き出す基本的な方法を見てみよう。



join()を使った改行方法

Pythonの文字列の改行方法と便利な操作まとめ | HEADBOOST

ここでは、Pythonの文字列の改行について、「\」や「\n」の扱いなどの最低限知っておくべき知識と、その他、改行を活用した便利な文字列操作について詳しく解説しています。



join()とmap()の使い方



リスト型⇔文字列型 変換













コメント

よく読まれている記事

CSSボタンでテキストを天地中央に揃えるとき、なぜボタン高と行高を一緒にするのか

FullCalendarの導入からカレンダー毎の色指定まで

FacebookページのフィードURLを取得しウォールを自サイトに表示