M.Hiroi's Home Page

Python3 Programming

お気楽 CustomTkinter 超入門

[ PrevPage | Python3 | NextPage ]

テキストウィジェット

●はじめに

いよいよ巨大なウィジェットである「テキストウィジェット」を説明します。エントリーウィジェットがラインエディタとするならば、テキストウィジェットはスクリーンエディタに相当し、柔軟で高度なテキスト編集を行うことができます。

●テキストウィジェットの生成

CustomTkinter の場合、テキストウィジェットは CTkTextBox() で生成します。テキストウィジェットには標準動作が用意されていて、それだけでテキスト編集が可能になっています。マウスの操作は、左クリックでカーソル位置の変更、ドラッグで範囲の選択、ダブルクリックで単語の選択が行えます。また、トリプルクリックで行の選択、ドラッグで文字列の選択ができます。

CTkTextBox の場合、オプション width と height はピクセル単位となります。また、オプション state に disabled を設定すると、テキストの変更を禁止することができます。これはキーボートからの入力だけでなく、プログラムによる挿入や削除も禁止されるので、必要なテキストデータをウィジェットに挿入してから、state を disabled に設定してください。

それから、オプション wrap で行の折り畳みを設定することができます。none を指定すると折り畳みは行われません。char は文字の切れ目で、word は単語の切れ目で折り畳みます。オプション activate_scrollbars でスクロールバーの 有効 / 無効 を指定することができます。デフォルトは True (有効) です。

●位置の指定

多くのメソッドで位置の指定が必要になります。基本的な指定方法を下表に示します。

表 : 位置の基本指定
N.MN 行の M 文字目
@x,yテキスト内の (x,y) の位置にある文字
endテキスト末尾
マーク名その名前のマークをつけた位置
タグ名.firstその名前のタグの最初の位置
タグ名.lastその名前のタグの最後の位置

マークとタグについてはあとで詳しく説明します。テキストウィジェットでは、行は 1 から数えますが、文字は 0 から数えるので注意してください。この基本指定に加えて、次に示す相対指定を組み合わせることができます。

表 : 位置の相対指定
+Nchars, -Ncharsそこから N 文字先、手前
+Nlines, -Nlinesそこから N 行先、手前
linestart, lineendその行の先頭、末尾
wordstart, wordendその単語の先頭、末尾

●テキストファイルを表示する

テキストウィジェットは多機能なので、ほかのウィジェットに比べて使いこなすのはちょっと難しいと思います。ですが、テキストを表示するだけならば、とても簡単にプログラムすることができます。まず最初に、テキストファイルを表示するプログラムを作ってみましょう。次のリストを見てください。

リスト : テキストファイルを表示する

import customtkinter as ctk
from CTkMenuBar import CTkMenuBar, CustomDropdownMenu
import tkinter as tk
import tkinter.filedialog as fd
import sys, os.path

# MainWindow
root = ctk.CTk()
my_font = ('Noto Sans Mono CJK JP', 16)
root.title('Text Viewer')

# Global
path_name = os.getcwd()

# File Select
def load_file():
    global path_name
    filename = fd.askopenfilename(filetypes = [('Text Files', ('.txt', '.py'))],
                                  initialdir = path_name)
    if filename != "":
        path_name = os.path.dirname(filename)
        fi = open(filename)
        t0.delete('1.0', 'end')
        for x in fi:
            t0.insert('end', x)
        fi.close()
        t0.mark_set(tk.INSERT, '1.0')
        t0.focus_set()

# メニュー
m0 = CTkMenuBar(root);
m1 = m0.add_cascade('File', font = my_font)

m2 = CustomDropdownMenu(widget = m1)
m2.add_option(option = "Open", command = load_file)
m2.add_separator()
m2.add_option(option = "Exit", command = root.quit)

# Text
t0 = ctk.CTkTextbox(root, width = 800, height = 400, font = my_font)
t0.pack()

root.mainloop()

メニューの設定とファイルの選択は イメージとファイルの選択 で作成した画像ローダーと同じです。テキストウィジェットは CTkTextbox() で生成します。これで垂直スクロールバーが付いたテキストウィジェットを生成することができます。

ファイルの読み込みは関数 load_file() で行います。askopenfilename() でファイル選択ダイアログを表示してファイル名を取得します。テキストウィジェットにデータを挿入するメソッドが insert() で、削除するメソッドが delete() です。まず、表示しているテキストを delete() で削除します。1.0 は 1 行目の 0 文字、つまりテキストの先頭を表します。

次に、open() でファイルをリードオープンし、1 行ずつデータを入力していきます。insert() の位置指定は end なので、データはテキストウィジェットの最後に追加されます。最後に close() でファイルを閉じて、focus_set() でフォーカスを設定します。

テキストウィジェット テキストウィジェット

ファイルの表示 ファイルの表示

●行番号の挿入と削除

テキストを表示するだけでは面白くないので、今度は行番号を表示してみましょう。メニューに次の項目を追加します。

m2.add_separator()
m2.add_option(option = "Number", command = change_number)
m2.add_separator()

グローバル変数 line_number が False の場合、行番号は表示されていないこととします。True の場合は行番号が表示されています。行番号の処理は関数 change_number() で行います。

リスト : 行番号の挿入と削除

def change_number():
    global line_number
    line = int(float(t0.index('end')))
    if not line_number:
        for x in range(1, line):
            t0.insert('{}.0'.format(x), '{:6d}:'.format(x))
        line_number = True
    else:
        for x in range(1, line):
            t0.delete('{}.0'.format(x), '{}.7'.format(x))
        line_number = False

最初に行数を index() メソッドで求めます。index() はテキストの位置を line.char の形式の文字列で返します。end を指定することで最終行の次の行を求めることができます。たとえば、ファイルの行数が 55 行であれば、index('end') は '56.0' を返します。int(float('56.0')) で整数値に変換すればファイルの行数を求めることができます。

行番号は行の先頭に挿入することで表示します。変数 x は行番号を表し、挿入する文字列を format で作成しています。この場合、先頭に空白を含めて 7 文字挿入することになります。そして、line_number を True に書き換えます。行番号の削除は行の先頭から 7 文字削除するだけです。テキストウィジェットのメソッドで範囲指定を指定する場合、終了位置の文字は範囲に含まれません。ご注意くださいませ。これで 0 から 6 文字目までの 7 文字が削除されます。そして、line_number を False に書き換えます。

最後に、関数 load_file() を修正します。ファイルを読み込んだあと、無条件に line_number を False にセットします。これでプログラムは完成です。

行番号の表示 行番号の表示

●プログラムリスト

リスト : 行番号の表示

import customtkinter as ctk
from CTkMenuBar import CTkMenuBar, CustomDropdownMenu
import tkinter as tk
import tkinter.filedialog as fd
import sys, os.path

# MainWindow
root = ctk.CTk()
my_font = ('Noto Sans Mono CJK JP', 16)
root.title('Text Viewer')

# Global
path_name = os.getcwd()
line_num = False

# Change Number
def change_number():
    global line_number
    line = int(float(t0.index('end')))
    if not line_number:
        for x in range(1, line):
            t0.insert('{}.0'.format(x), '{:6d}:'.format(x))
        line_number = True
    else:
        for x in range(1, line):
            t0.delete('{}.0'.format(x), '{}.7'.format(x))
        line_number = False

# File Select
def load_file():
    global path_name, line_number
    filename = fd.askopenfilename(filetypes = [('Text Files', ('.txt', '.py'))],
                                  initialdir = path_name)
    if filename != "":
        path_name = os.path.dirname(filename)
        fi = open(filename)
        t0.delete('1.0', 'end')
        for x in fi:
            t0.insert('end', x)
        fi.close()
        t0.mark_set(tk.INSERT, '1.0')
        t0.focus_set()
        line_number = False

# メニュー
m0 = CTkMenuBar(root);
m1 = m0.add_cascade('File', font = my_font)

m2 = CustomDropdownMenu(widget = m1)
m2.add_option(option = "Open", command = load_file)
m2.add_separator()
m2.add_option(option = "Number", command = change_number)
m2.add_separator()
m2.add_option(option = "Exit", command = root.quit)

# Text
t0 = ctk.CTkTextbox(root, width = 800, height = 400, font = my_font)
t0.pack()

root.mainloop()

テキストウィジェット : マークとタグ

●はじめに

テキストウィジェットの最大の特徴は、特定の位置をマークしたり、特定の文字列にタグをつけ、フォントや色といった属性の変更やバインディングの設定が可能なことです。CTkTextbox は内部的に標準の tkinter.Text ウィジェットを保持しているため、基本的な操作は Tkinter のそれとほぼ同じです。

なお、tkinter.Text ウィジェットは、キャンバスウィジェットと同様にテキストの中にウィジェットを表示することができるのですが、CtkTextbox ではサポートされていないようです。ご注意ください。

●マーク

それではマークから説明しましょう。マークはテキストの位置を表す名前のことです。マークは文字自体につけられるのではなく、文字と文字の間に設定されます。このため、マークで指定した位置に文字列を挿入する場合はとても便利です。また、テキストを操作するメソッドで、位置の指定にマークを使うこともできます。

マークを操作するおもなメソッドを表に示します。

表 : マーク操作用のおもなメソッド
mark_set(markname, index)マークの設定
mark_unset(*markname)マークの削除
mark_names()定義されているすべてのマークを返す
mark_gravity(markname, left_or_right)マークのつき方を left と right で指定
mark_next(index)index より後ろにあるマークを返す
mark_previous(index)index より前にあるマークを返す

マークの設定は mark_set() メソッドで行います。マークは指定した位置の文字とその前の文字の間に設定されます。たとえば '1.3' と指定すると、1 行目の 2 文字目と 3 文字目の間にマークが設定されます。文字は 0 から数えることに注意してください。たとえば、テキストウィジェットの 1 行目に abcdefg が書き込まれている状態で、次のように first という名前のマークを設定します。

t0.mark_set(first, '1.3')

変数 t0 はテキストウィジェットのオブジェクトです。これで、マーク first は 1 行目の 2 文字目 (c) と 3 文字目 (d) の間に設定されます。この状態で 1 行目の先頭文字 a を削除すると文字 d は 2 文字目になるので、first の位置は 1.3 ではなく 1.2 に変わります。また、行頭に文字 A を挿入すれば d は 4 文字目になるので、first は 1.3 から 1.4 に変わります。このように指定した文字 d が移動すれば、その文字とともにマークも移動するわけです。

マークの位置に文字列を挿入する場合、マークは挿入した文字列の左右どちらかにつきます。mark_gravity() メソッドは、文字列を挿入したときのマークのつき方を指定します。left であれば挿入した文字列の左側に、right であれば右側にマークが設定されます。たとえば、first の位置に文字列 1234 を挿入するには、次のように行います。

t0.insert(first, '1234')

first の位置が c と d の間であれば、文字列は abc1234defg となります。このとき、マークが挿入した文字列の左側につく場合は c と 1 の間にマークが設定されます。逆に、右側につく場合は 4 と d の間に設定されます。デフォルトの設定は right なので、ここでもう一度 first に文字列 5678 を挿入すると、文字列は abc12345678defg となります。

それから、特別なマークとしてカーソル位置を表す insert と、マウスカーソルが指す文字位置を表す current があります。たとえば、カーソルの位置に文字列を挿入したい場合は、次のように行います。

t0.insert(insert, 'string')

これで指定した string がカーソル位置に挿入されます。

●タグ

テキストウィジェットのタグはキャンバスウィジェットのタグと同様に、指定した文字列に名前 (タグ名) をつける機能です。そして、タグごとにフォントや色などの表示属性やバインディングを設定することができます。この機能により、テキストウィジェットは単なるテキスト編集だけではなく、ある単語をクリックしたら別のテキストを表示する、といったハイパーテキストを構成することができます。

Tkinter では、タグを操作するメソッドが多数用意されています。タグの設定、削除、検索といった基本的な機能のほかに、オプションやバインディングの設定を行うことができます。フォント、色、アンダーラインなどの表示属性はオプションで設定します。おもなメソッドを表に示します。

表 : タグ操作用のおもなメソッド
tag_add(tagname, index1, index2)指定した範囲に対して、タグ tagname を設定
tag_delete(*tagname)タグの削除
tag_names(index)index の位置にある文字と関連するすべてのタグを返す
tag_cget(tagname, option)タグ tagname のオプションの値を返す
tag_config(tagname, option, value)タグ tagname のオプションを設定する
tag_bind(tagname, event, callback)タグ tagname にバインドを設定する

Tkinter ではオプションの設定に tag_configure() を使いますが、CustomTkinter では tag_config() を使います。また、オプションも Tkinter の名前を使います。たとえば、foreground (文字色), background (背景色), font, justify (揃え), underline (下線) などが指定可能です。

このほかにも、いろいろなメソッドやオプションが用意されています。詳細は Tkinter のマニュアルを参照してください。

●行番号に色を付ける

それでは簡単な例題を示します。前回作成したテキストを表示するプログラムで、行番号を赤く表示してみましょう。行番号を表す文字列にタグ LINENUM を指定し、色をオプションで設定します。

オプションは tag_config() で設定します。タグ LINENUM の設定は次のようになります。

t0.tag_config('LINENUM', foreground = 'red')

Tkinter の場合、文字の色は foreground で指定します。text_color はエラーになるので注意してください。また、background で背景色も指定することができます。

行番号にタグを設定することはとても簡単です。insert() メソッドでタグを指定するだけです。

t0.insert(index, string, tagname, ...)

タグを指定すると、挿入した文字列にそのタグが設定されます。したがって、前回作成した change_number() を次のように修正するだけです。

リスト : 行番号の挿入と削除

def change_number():
    global line_number
    line = int(float(t0.index('end')))
    if not line_number:
        for x in range(1, line):
            t0.insert('{}.0'.format(x), '{:6d}:'.format(x), 'LINENUM')
        line_number = True
    else:
        for x in range(1, line):
            t0.delete('{}.0'.format(x), '{}.7'.format(x))
        line_number = False

このように、挿入する文字列の後ろにタグ名 LINENUM を指定します。これで行番号が赤く表示されます。

テキストの表示 テキストを表示する

行番号を赤く表示 行番号を赤く表示する

●埋め込みウィンドウは使えない

ところで、Tkinter のテキストウイジェットはキャンバスウィジェットと同様に、テキストだけではなくほかのウィジェットも表示することができます。これを「埋め込みウィンドウ」といいます。CustomTkinter の CtkTextbox の場合、埋め込みウィンドウの機能は直接サポートされていません。テキスト内にボタンやチェックボックスなどの他ウィジェットを流し込むことは現時点では困難なようです。どうしてもテキストにウィジェットを埋め込みたい場合は、Tkinter のテキストウィジェットを使ったほうがよいでしょう。


●プログラムリスト

リスト : 行番号に色を付ける

import customtkinter as ctk
from CTkMenuBar import CTkMenuBar, CustomDropdownMenu
import tkinter as tk
import tkinter.filedialog as fd
import sys, os.path

# MainWindow
root = ctk.CTk()
my_font = ('Noto Sans Mono CJK JP', 16)
root.title('Text Viewer')

# Global
path_name = os.getcwd()
line_num = False

# Change Number
def change_number():
    global line_number
    line = int(float(t0.index('end')))
    if not line_number:
        for x in range(1, line):
            t0.insert('{}.0'.format(x), '{:6d}:'.format(x), 'LINENUM')
        line_number = True
    else:
        for x in range(1, line):
            t0.delete('{}.0'.format(x), '{}.7'.format(x))
        line_number = False

# File Select
def load_file():
    global path_name, line_number
    filename = fd.askopenfilename(filetypes = [('Text Files', ('.txt', '.py'))],
                                  initialdir = path_name)
    if filename != "":
        path_name = os.path.dirname(filename)
        fi = open(filename)
        t0.delete('1.0', 'end')
        for x in fi:
            t0.insert('end', x)
        fi.close()
        t0.mark_set(tk.INSERT, '1.0')
        t0.focus_set()
        line_number = False

# メニュー
m0 = CTkMenuBar(root);
m1 = m0.add_cascade('File', font = my_font)

m2 = CustomDropdownMenu(widget = m1)
m2.add_option(option = "Open", command = load_file)
m2.add_separator()
m2.add_option(option = "Number", command = change_number)
m2.add_separator()
m2.add_option(option = "Exit", command = root.quit)

# Text
t0 = ctk.CTkTextbox(root, width = 800, height = 400, font = my_font)
t0.tag_config('LINENUM', foreground = 'red')
t0.pack()

root.mainloop()

CTkTabview

CTkTabview は複数のウィジェットを格納し、表示するウィジェットをタブで切り替えるウィジェットです。いわゆる「タブブラウザ (Tabbed Browser)」と同様の動作をします。ウィジェットは CTkTabview() で生成します。主なオプションを以下に示します。

主なメソッドを以下に示します。

基本的な使い方は簡単です。メソッド add("タブ名") でタブを作成します。そして、メソッド tab("タブ名") でフレームを取得し、そこにボタンやラベルなどのウィジェットを配置します。

簡単な使用例と実行結果を示します。

リスト : CTkTabview の使用例

import customtkinter as ctk

root = ctk.CTk()

# Tabviewの作成
tabview = ctk.CTkTabview(root, width=300, height=200)
tabview.pack(padx=4, pady=4)

# タブの追加
tabview.add("Tab 1")
tabview.add("Tab 2")

# タブ1にウィジェットを配置
label_1 = ctk.CTkLabel(tabview.tab("Tab 1"), text="これは Tab 1 の内容です")
label_1.pack(padx=10, pady=10)

# タブ2にウィジェットを配置
button_2 = ctk.CTkButton(tabview.tab("Tab 2"), text="Tab 2 のボタン")
button_2.pack(padx=10, pady=10)

# 初期表示タブの設定(任意)
tabview.set("Tab 1")

root.mainloop()

Tab 1 を選択

Tab 2 を選択


CTkProgressBar

プログレスバー (progressbar) は時間がかかる処理で進捗状況を表示するためのウィジェットです。水平方向のバーで進捗率を表すものが一般的です。CustomTkinter の場合、プログレスバーのウィジェットは CTkProgressbar() で生成します。主なオプションを以下に示します。

主なメソッドを以下に示します。

簡単な使用例と実行結果を示します。

リスト : indeterminate の使用例

import customtkinter as ctk

root = ctk.CTk()

# modeを"indeterminate"に設定
pb = ctk.CTkProgressBar(root, mode="indeterminate")
pb.pack(padx=20, pady=20)

# アニメーション開始
pb.start()

root.mainloop()

バーが左端にある状態

バーが右端にある状態

リスト : determinate の使用例

import customtkinter as ctk

# 定期的な更新にはメソッド root.after(ms, callback) を使用する
def animate_progress(val):
    if val <= 1.0:
        # 進捗をセット (0.0 ~ 1.0)
        pb.set(val)
        root.after(10, lambda: animate_progress(val + 0.01))

root = ctk.CTk()

pb = ctk.CTkProgressBar(root, mode="determinate")
pb.pack(pady=20, padx=20)

# 初期値
pb.set(0)

button = ctk.CTkButton(root, text="Start", command = lambda: animate_progress(0))
button.pack(pady=10)

root.mainloop()

初期状態

最終状態


Copyright (C) 2026 Makoto Hiroi
All rights reserved.

[ PrevPage | Python3 | NextPage ]