【Tkinter】Spinbox・15メソッドの実践テクニックを解説!

この記事の内容

  • Spinboxとは?
  • Spinboxのメソッドには何がある?
  • Spinboxのメソッドの使い方は?

 

こんにちは、Youta(@youta_blog)です。

 

今回は、TkinterのSpinboxのお話です。

特にSpinboxクラスのメソッドを深掘りしていきます。

 

すずか

Spinboxってオプションの情報はネットに溢れてるけど、メソッドは情報少ないよね〜。
そうですね。そこで今回は敢えてSpinboxのメソッドに特化して情報を集めましたよ。

先生

 

ウィジェットで実現したいことがオプションではできない…。

そんな時は、メソッドにも目を配ると解決の糸口を掴めるかも知れません。

 

本記事では、解説・確認用コード・実行結果の3点セットをメソッドごとに用意しております。

また必要に応じてコードの解説をしていますので、初心者でも安心です。

 

記事の信頼性は、確認用コードと実行結果が保証します。

少しでも疑問に感じたら、是非ともコードをコピペしてPCで確かめてみてください。

 

さあ、一緒にSpinboxのメソッドを探求する旅に出掛けましょう。

Spinboxの基礎情報

Spinboxとは

 

TkinterのSpinbox(スピンボックス)は、文字列・数値を入力するためのウィジェットです。

実質、Entryに上下ボタンがついただけです。

上下ボタンにより項目を選択できるため、入力エラー防止が期待できます。

Spinboxを作る構文

TkinterでSpinboxを作るには、以下の文法を押さえましょう。

spinbox = Spinbox(master, option, ...)

 

重要なのは2つだけです。

  • master: 親ウィンドウ(またはフレーム)。(ほぼ)必須。
  • option: 動作や外観をカスタマイズする引数。任意。

Spinboxの作成

実際は次のようにSpinboxを作ります。


import tkinter as tk


root = tk.Tk()  # 親ウィンドウ作成

spinbox = tk.Spinbox(
    root,
    from_=0,  # 最小値0
    to=10,  # 最大値10
)
spinbox.pack()

root.mainloop()  # 画面表示

 

ここがSpinboxを作る処理です。

spinbox = tk.Spinbox(
    root,
    from_=0,  # 最小値0
    to=10,  # 最大値10
)

先程のmasterに相当するrootは、親ウィンドウの変数名です。

from_とtoは、Spinboxの設定を細かく指定するオプションです。

オプションは任意なので、なくても一応Spinboxは作れます。

 

次に、作成したSpinboxを配置します。

私達に見えるようにする処理ですね。

spinbox.pack()

配置の関数はpackとgridとplaceの3種類あります。

一番簡単そうなpackを使いました。

これがないと、せっかく作ったSpinboxが表示されないのでご注意ください。

 

コードを実行すると、Spinboxを乗せた小さい画面が出現します。

0〜10で数値を選択できます。

from_・toオプションのおかげです。

 

いかがでしょうか。

イメージを掴んでいただけたら幸いです。

Spinboxの主要メソッド

ここでは、Spinboxクラスのメソッドをご紹介します。

Spinboxクラスのメソッド

 

すずか

いやめっちゃあるじゃん。
初心者の方は、まずは次の3つを押さえていただければと!

先生

【厳選】Spinboxクラスのメソッド

.get()

getメソッドはSpinbox内の文字を取得します。

引数はありません。

 

例えば、Spinboxの中身が’12345’だとします。

 

このとき、次のコードを実行すれば’12345’を取得できるイメージです。

current_value = spinbox.get()

print(current_value)
# 12345

print(type(current_value))
# <class 'str'>

 

実演してみましょう。

Spinboxに文字を入力してEnterを押すと、Spinbox内の文字列を出力するプログラムです。


import tkinter as tk


def output(event):
    text = spinbox.get()
    print(text)


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.bind('<Return>', output)
spinbox.pack()

root.mainloop()

 

Spinboxに、Enterの押下を検知する仕組みを作りました。

また検知後は後ろのoutput関数を実行させます。

spinbox.bind('<Return>', output)

ちなみに'<Return>’はEnter押下を表します。

この文字列は、イベントシーケンスと呼ばれます。

余力があれば覚えましょう。

 

すずか

bindって何?
簡単にいうと「ユーザーの動作を検知して、何かの処理を実行させる機能」ですよ。

先生

.bind(
    検知して欲しい動作(イベントシーケンス),
    実行して欲しい処理(関数名やlambda式),
)

いずれbindに関する詳しい記事を書きますね。

 

output関数の中身です。

def output(event):
    text = spinbox.get()
    print(text)

getメソッドでSpinbox内の文字列を取得して、text変数に入れます。

最後にprint関数でtextを出力する流れです。

 

例として、以下の動作を順に行いましょう。

  1. 起動する。
  2. ‘Tkinter’と入力する。
  3. Enterキーを押す。
  4. ‘Tkinter’を消す。
  5. ’Spinbox’と入力する。
  6. Enterキーを押す。

 

まずは画面の様子をご覧ください。

Enterの押下が動画では伝わらないですね…。

 

ターミナルに以下が表示されていればOKです。

Tkinter
Spinbox

.insert(index, string)

insertメソッドはSpinboxに文字や数字を挿入します。

 

引数は次の2つです。

  • index: 文字列を挿入する場所の番号
  • string: 挿入する文字列

 

すずか

indexの説明意味わからん。文字列を挿入する場所の番号って何?
実は文字列の各文字には番号が付いているんですよ。

先生

 

例えば、Spinboxの中身が’Tkinter’だとします。

 

このとき、文字に番号が割り振られます。

例えば、’T’は0番目で’n’は3番目にあります。

 

すずか

じゃあ、’i’と’n’の間に文字列を入れるにはinsert(2.5, 文字列)ってすればいいのかな?
と思いますよね。

しかしinsert(3, 文字列)なんです。

先生

すずか

えー!

 

ここが初学者が躓きやすいポイントです。

nとn+1番の文字間に文字列を挿入するには、insert(n+1, 文字列)とします。

 

この説明でパッと分かれば問題ありません。

しかし、混乱した方は一旦次の図を見ると良いでしょう。

単に番号を左にシフトしただけです。

文字の順番よりも、むしろ仕切りの番号と捉えると分かりやすいかも知れません。

こうすれば「index=3」が’i’と’n’の間を指すのに納得がいくかと思います。

 

確かめてみましょう。

‘Tkinter’と入力してEnterを押すと、’i’の後ろに’12345’を入れるプログラムです。


import tkinter as tk


def insert_text(event):
    spinbox.insert(3, '12345')

root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.bind('<Return>', insert_text)
spinbox.pack()

root.mainloop()

 

Enterを押すと、insert_text関数を実行します。

spinbox.bind('<Return>', insert_text)

 

insert_text関数の中身です。

def insert_text(event):
    spinbox.insert(3, '12345')

実態はSpinboxのinsertメソッドですね。

3番の文字の直前に’12345’を挿入します。

 

実行結果です。

.delete(first, last=None)

deleteメソッドはSpinbox内の文字列を削除します。

 

引数は必須と任意が1つずつあります。

  • first: 削除を始める場所の番号。必須。
  • last: 削除を終える場所の番号。任意。

 

文字列の各文字に振られる番号の説明はinsertメソッドでしましたね。

deleteメソッドも、引数で渡す番号を仕切りで捉えると分かりやすいです。

 

例えば、Spinboxの中身が’Tkinter’だとします。

 

このとき「first=2」・「last=6」とすると、2番から6番の仕切りに挟まれた文字列’inte’が削除されます。

 

ちなみに「last=’end’」とすると、firstから最後まで消せますよ。

先生

 

確かめてみましょう。

Enterを押すと、’Tkinter’という文字列から’inte’を消すプログラムです。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Return>', lambda e: spinbox.delete(2, 6))
spinbox.pack()

root.mainloop()

 

先程学んだinsertメソッドを取り入れてみました。

これにより、起動時に自動で’Tkinter’という文字列が入ります。

spinbox.insert(0, 'Tkinter')

挿入位置の番号に関しては、初めはSpinbox内に文字が何もないので0を入れます。

 

Enterの押下を検知したら、spinbox.delete(2, 6)を実行させます。

spinbox.bind('<Return>', lambda e: spinbox.delete(2, 6))

 

すずか

‘<Return>’の後が謎すぎる。関数名が入るんじゃないの?
lambda式は一種の関数です。以下のコードと同じですよ。

先生

def delete_text(event):
    spinbox.delete(2, 6)

spinbox.bind('<Return>', delete_text)

すずか

よくわからないけど、省略した書き方なんだね。

 

lambda式も需要があれば記事にします。

 

実行結果です。

 

すずか

あれ、lastって任意だよね。なかったらどう消すの?
lastが指定されない場合、firstからfirst+1までの文字が消えます。

先生

 

例えば「first=2」だけでlastが指定されなかったとき、「last=3」と処理されます。

 

確かめてみましょう。

Enterを押すと、’Tkinter’という文字列から’i’を消すプログラムです。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Return>', lambda e: spinbox.delete(2))
spinbox.pack()

root.mainloop()

 

実行結果です。

.index(index)

indexメソッドはSpinbox内の文字列のインデックスを取得できます。

「〇〇な番号はど〜れだ?」と聞くとその番号を教えてくれる関数です。

その「〇〇」を引数として渡してあげます。

 

引数に指定できるのは、次の5つのどれかです。

tk.END

tk.ENDをinsertメソッドで使用すると、Spinbox内の文字列の末尾の位置を番号で取得できます。

言ってしまえば文字列の長さです。

 

例えば、Spinboxの中身が’Tkinter’だとします。

 

このとき、indexメソッドにtk.ENDを渡すと、文字列最後の仕切りの番号を取れます。

またこの番号は文字数でもあるのです。

 

イメージとしてはこんな感じです。

index = spinbox.index(tk.END)

print(index)
# 7

print(type(index))
# <class 'int'>

 

確認してみましょう。

Spinboxに文字列を入力し、Enterを押すと文字列の長さを表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    end_index = spinbox.index(tk.END)
    label.config(text=f'End index: {end_index}')


root = tk.Tk()

label = tk.Label(root, text='End index: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.bind('<Return>', update_label_text)
spinbox.pack()

root.mainloop()

 

まず最初に、Spinboxとは異なるウィジェットであるLabelの登場です。

label = tk.Label(root, text='Insert index: ')
label.pack()

 

Labelは簡単に言うと、文字を書けるプレートだと思ってください。

このプレートに’Insert index: ‘と書きます。

いずれLabelの解説記事を出しますね。

 

次に、Spinboxにイベント設定をします。

Enterを押すとupdate_label_textが呼ばれます。

spinbox.bind('<Return>', update_label_text)

 

これがupdate_label_text関数の中身です。

def update_label_text(event):
    end_index = spinbox.index(tk.END)
    label.config(text=f'End index: {end_index}')

ここでindexメソッドを使っています。

同メソッドで取得した最後の文字番号を、そのままLabelに書き込む流れです。

Labelは’End index: 〇〇(番号)’と表示されます。

 

実行結果です。

tk.INSERT

tk.INSERTをindexメソッドで使用すると、挿入カーソルの位置を取得できます。

ちなみに挿入カーソルとは、スマホで文字を打つ時に出てくるあの縦の線です。

 

例えば、Spinboxの中身が’Tkinter’だとします。

カーソルを’i’と’n’の間に設定します。

 

indexメソッドにtk.INSERTを渡すと、挿入カーソルと重なる仕切りの番号を取れます。

 

テスト用コードで確かめてみましょう。

Spinbox上でマウスカーソルを動かすと、挿入カーソルの位置を表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    insert_index = spinbox.index(tk.INSERT)
    label.config(text=f'Insert index: {insert_index}')


root = tk.Tk()

label = tk.Label(root, text='Insert index: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

Spinboxにイベント設定をします。

spinbox.bind('<Motion>', update_label_text)

‘<Motion>’は、マウスカーソルが動くことを表すイベントシーケンスです。

Spinbox上でマウスカーソルを操作すると、update_label_text関数が発火します。

 

これがupdate_label_text関数の中身です。

Spinbox内の文字列に挿入カーソルがある位置をLabelに書いていく処理です。

def update_label_text(event):
    insert_index = spinbox.index(tk.INSERT)
    label.config(text=f'Insert index: {insert_index}')

 

ここでindexメソッドを使っています。

    insert_index = spinbox.index(tk.INSERT)

 

indexメソッドで取得した番号は、そのままLabelに書き込む流れです。

    label.config(text=f'Insert index: {insert_index}')

挿入カーソルの位置を正しく取得できるかどうかが分かりますね。

 

ちなみに、Labelは’Insert index: 〇〇(番号)’と表示されます。

先程の例のように、挿入カーソルが’i’と’n’の間にあれば、’Insert index: 3’となります。

 

実行結果をどうぞ。

tk.ANCHOR

tk.ANCHORをindexメソッドで使用すると、Spinbox内で最後にクリックした文字の位置を教えてくれます。

 

すずか

え 最後にクリックした位置って絶対挿入カーソル入るよね?

そしたらtk.INSERTと返す番号が一緒じゃないの?

そう思いますよね。

せっかくなので、tk.ANCHORとtk.INSERTの違いを調べましょう。

先生

 

以下のコードを書きます。

indexメソッドにtk.INSERTとtk.ANCHORの2つを渡して比較しましょう。


import tkinter as tk


def update_label_text(event):
    insert_index = spinbox.index(tk.INSERT)
    anchor_index = spinbox.index(tk.ANCHOR)
    label.config(text=f'Insert index: {insert_index}, Anchor_index: {anchor_index}')


root = tk.Tk()

label = tk.Label(root, text='Insert index: -, Anchor index: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

tk.INSERTのテストコードとほとんど一緒ですのでご安心ください。

 

大きく変わったのはこの関数内です。

tk.INSERTとtk.ANCHORを比較したいので、それぞれを引数にしてindexメソッドを2回呼びます。

def update_label_text(event):
    insert_index = spinbox.index(tk.INSERT)
    anchor_index = spinbox.index(tk.ANCHOR)

    label.config(text=f'Insert index: {insert_index}, Anchor_index: {anchor_index}')

取得した番号はそれぞれ別の変数に入れます。

「insert_index」と「anchor_index」です。

最後にその2つをラベルに書き込みます。

 

まずはtk.INSERTのときの実験と同じ手法で、クリックして番号を確認します。

 

すずか

ほら、やっぱり同じじゃん。

 

tk.ANCHORは、最後にクリックした地点の番号を取得できます。

この実験では、挿入カーソルはクリックにより移動します。

つまり、現在の挿入カーソルの位置(tk.INSERT)と最後のクリック位置(tk.ANCHOR)が一緒なのは当然なのです。

 

では、クリックせずに挿入カーソルを動かせたらどうでしょうか?

先生

 

実は、キーボード右下の「→」「←」キーでも挿入カーソルの位置を動かせるのです。

これで再実験してみましょう。

違いが確認できましたね。

 

繰り返しますが、tk.ANCHORは直近のクリック地点を指します。

初めに「0」→「7」にクリックでカーソルを動かした後は、カーソルの移動は「←」キーのみで行っています。

つまり、クリックを行っていないので’Anchor index’は「7」のままなのです。

 

対して、tk.INSERTは挿入カーソルの動きに呼応して番号がコロコロ変わります。

純粋に挿入カーソルの位置を追うからですね。

 

すずか

すっきりした!

 

なお、文字列の更新はマウスカーソルの動きに反応させているので、終始マウスを動かしています。

入力欄の真ん中で縦線の記号が挙動不審なのはそのためです。

tk.SEL_FIRST

tk.SEL_FIRSTをindexメソッドで使用すると、Spinbox内で文字列を選択した際の選択開始地点を教えてくれます。

ちなみに、文字選択のない状態でtk.SEL_FIRSTを使うとエラーが出るのでご注意を。

 

文字の選択とは次のような状態です。

 

例えば’Tkinter’という文字列のうち、’inter’を選択すると「2」が取れます。

 

確認してみましょう。

現在選択している文字の先頭の番号を表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    index = -1

    try:
        sel_first_index = spinbox.index(tk.SEL_FIRST)
    except:
        index = '-'

    if index == -1:
        index = sel_first_index

    label.config(text=f'Sel first index: {index}')


root = tk.Tk()

label = tk.Label(root, text='Sel first index: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

Spinbox上でマウスカーソルが動くたびに、次の関数が呼ばれます。

def update_label_text(event):
    index = -1

    try:
        sel_first_index = spinbox.index(tk.SEL_FIRST)
    except:
        index = '-'

    if index == -1:
        index = sel_first_index

    label.config(text=f'Sel first index: {index}')

 

初めにindex変数に-1を入れておきます。

    index = -1

 

tk.SEL_FIRSTは、文字列選択がされていないとエラーが出ます。

そのため、try-except文でエラーを出さないように一工夫。

    try:
        sel_first_index = spinbox.index(tk.SEL_FIRST)
    except:
        index = '-'

もしエラーが出たら(文字列が選択されていなければ)、indexに文字’-‘を入れます。

先程入れた-1はガン無視して上書きです。

 

お次はindexの中身で判定処理。

    if index == -1:
        index = sel_first_index

もしindexが-1のままだったら、とはどういうことでしょうか。

それは、先程のtry-except文でindexが-1から’-‘に上書きされていないということです。

 

indexが塗り替えられるのは、エラーが出るときでしたよね。

つまり、indexが-1とは、tk.SEL_FIRSTが上手く動作したのを意味します。

try文内のsel_first_indexに、選択文字列の開始地点の番号が入ったと言ってもいいですね。

それをindexに入れてやろうというわけです。

 

最後はLabelを更新します。

    label.config(text=f'Sel first index: {index}')

エラーが出ていない(=文字列が選択状態)場合は、’Sel first index: 〇〇(番号)’。

エラーが出た(=文字列が選択されていない)場合は、’Sel first index: -‘。

 

実験してみましょう。

tk.SEL_LAST

tk.SEL_LASTをindexメソッドで使用すると、Spinbox内で文字列を選択した際に選択終了地点を教えてくれます。

これもtk.SEL_FIRSTと同様、文字列選択がされていない状態だとエラーが出ます。

 

念の為、文字の選択とは次のような状態です。

 

例えば’Tkinter’という文字列のうち’inter’を選択すると、「7」が取れます。

 

確認してみましょう。

現在選択している文字の末尾の番号を表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    index = -1

    try:
        sel_last_index = spinbox.index(tk.SEL_LAST)
    except tk.TclError:
        index = '-'

    if index == -1:
        index = sel_last_index

    label.config(text=f'Sel last index: {index}')


root = tk.Tk()

label = tk.Label(root, text='Sel last index: -)
label.pack()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

tk.SEL_FIRSTのテストコードと9割同じです。

解説は割愛します。

 

実行結果です。

’@x’

‘@x’をindexメソッドで使用すると、指定した\(x\)座標に最も近い文字の位置を取得できます。

 

とはいっても、’@x’とそのまま引数に渡すのではありません。

‘x’に\(x\)座標を整数で指定します。

 

例えば、ある点と文字列の位置関係が次の図のようなとき。

点の\(x\)座標は5ですね。

このとき、indexメソッドに’@5’という文字列を渡すと、その\(x\)座標に最も近い文字の番号が取れます。

点の最寄りの文字は’n’なので、戻り値は3です。

 

処理のイメージはこんな感じです。

index = spinbox.index('@5')

print(index)
# 3

print(type(index))
# <class 'int'>

ただし図の座標はテキトーなので、実際にindex(‘@5’)で3が返るかは不明です。

 

ちなみに’T’より左側は0、’r’より右側は7と出ます。

先生

 

それでは確認してみましょう。

マウスカーソルに最も近い文字の番号を表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    x_coordinate = event.x
    nearest_index = spinbox.index(f'@{x_coordinate}')

    label.config(text=f'The nearest index: {nearest_index}')


root = tk.Tk()

label = tk.Label(root, text='The nearest index: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

Spinbox上で、マウスを動かすたびに次の関数が呼ばれます。

def update_label_text(event):
    x_coordinate = event.x
    nearest_index = spinbox.index(f'@{x_coordinate}')

    label.config(text=f'The nearest index: {nearest_index}')

 

まずはマウスカーソルの\(x\)座標を取得。

    x_coordinate = event.x

 

f文字列により、先程の\(x\)座標を’@’もろとも文字列にし、indexメソッドに渡します。

    nearest_index = spinbox.index(f'@{x_coordinate}')

 

最後にLabelの更新をします。

    label.config(text=f'The nearest index: {nearest_index}')

‘The nearest index: 〇〇(番号)’と表示されます。

「最も近い番号: 〇〇(番号)」という意味です。

 

実行結果です。

.icursor(index)

icursorメソッドはSpinbox内で指定した位置に挿入カーソルを設定します。

注意点として、Spinboxにフォーカスが当たっていないと作用しません。

 

フォーカスとは

ウィジェットがユーザーからのキー入力や操作を受け付ける状態です。

 

フォーカスが当たると枠が出てきますね。

 

例えば、’Tkinter’という文字列の’i’と’n’の間に挿入カーソルを入れたいとき。

クリックで入れることもできますが、icursor(3)で入れることもできるのです。

 

確かめてみましょう。

Enterを押すと、挿入カーソルを強制的に’i’と’n’の間にセットするプログラムです。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.focus_set()
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Return>', lambda e: spinpox.icursor(3))
spinbox.pack()

root.mainloop()

 

icursorメソッドはSpinboxにフォーカスがないと動かないので、事前に自動でフォーカスを設定しておきます。

spinbox.focus_set()

起動直後に手動でフォーカスを当てる場合、この処理は不要です。

 

Enter押下で呼ばれる関数がspinbox.icursor(3)です。

spinbox.bind('<Return>', lambda e: spinpox.icursor(3))

 

lambda式初見の方は、こちらのコードと等価だと思ってください。

def set_cursor_position(event):
    spinbox.icursor(3)

spinbox.bind('<Return>', set_cursor_position)

 

挙動を確認しましょう。

挿入カーソルをどこへ持っていっても、Enterを押すだけで瞬時に’i’と’n’の間に戻りますね。

.invoke(element)

invokeメソッドはSpinbox内で特定のアクションを呼び出します。

特定のアクションとは、Spinbox右横の「上ボタン押下」or「下ボタン押下」です。

つまり、自分でクリックせずともこの関数で上下ボタンを押したことにできるのです。

 

引数は次の3つから選べます。

‘buttonup’

‘buttonup’をinvokeメソッドで使用すると、Spinbxの上ボタン押下と同じ働きをします。

 

確かめてみましょう。

まずはinvokeメソッドを使わずに起動します。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root, from_=0, to=10)
# spinbox.invoke('buttonup')
spinbox.pack()

root.mainloop()

 

spinbox = tk.Spinbox(root, from_=0, to=10)

Spinboxの範囲を0〜10までとしています。

from_は最小値、toは最大値を指定するオプションです。

 

最初は最小値の0が表示されるようです。

 

ここで、invokeメソッドのコメントアウトを解除しましょう。

行頭の’#’を削除すればOKです。

spinbox.invoke('buttonup')

 

invokeメソッドありきでの実行結果です。

クリックせずとも1だけ繰り上がっていますね。

 

もちろん、for文で好きなだけ上ボタンを押したことにするのも可能です。

for _ in range(5):
    spinbox.invoke('buttonup')

 

上ボタンを5回連打した状態になりました。

‘buttondown’

‘buttondown’をinvokeメソッドで使用すると、Spinbxの下ボタン押下と同じ働きをします。

 

確かめてみましょう。

まずはinvokeメソッドをコメントアウトした状態から。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root, from_=0, to=10)
spinbox.delete(0, 'end')
spinbox.insert(0, '10')
# spinbox.invoke('buttondown')
spinbox.pack()

root.mainloop()

 

範囲を0〜10に設定すると、初期状態でSpinboxは0と表示されます。

’buttonup’のテストコード実行例で見た通りです。

 

最小値が0なので、’buttondown’で下ボタンを押しても結果は0のままになります。

そこで、最大値の10を初期状態で表示するように仕向けます。

spinbox.delete(0, 'end')
spinbox.insert(0, '10')

初めに、deleteメソッドでSpinbox内を真っ白に。

その後、insertメソッドで10を入れてやります。

 

0を消して10を入れたので、起動時は10です。

 

ここで、invokeメソッドのコメントアウトを解除しましょう。

行先頭の’#’を削除すればOKです。

spinbox.invoke('buttondown')

 

invokeメソッドありきでの実行結果です。

下ボタンを押さずとも1繰り下がりましたね。

‘none’

‘none’は、invokeメソッドで使用できますが、特に何もしません。

存在意義が問われる引数です。

 

試しにやってみましょうか。

先生

すずか

いや、いいよ。

 

気になる方のために、一応コードと結果を用意しました。

 

以下のコードを実行します。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root, from_=0, to=10)
spinbox.invoke('none')
spinbox.pack()

root.mainloop()

 

実行結果です。

初期状態と同じですね。

 

やってしまいました。

先生

すずか

やらなくてもいいのに〜。

.identify(x, y)

identifyメソッドは指定した座標にあるSpinbox内での部品を特定します。

 

大前提、Spinboxは3つの部品からなります。

 

例えば、座標\((8, 4), (27, 3), (28, 5)\)に着目しましょう。

上図の状態であれば、identifyメソッドに順に3つ座標を渡すと’entry’, ‘buttonup’, ‘buttondown’と文字列が取得できます。

 

イメージはこんな感じです。

element1 = spinbox.idntify(8, 4)
element2 = spinbox.identify(27, 3)
element3 = spinbox.identify(28, 5)

print(element1, element2, element3)
# entry buttonup buttondown

print(type(element1), type(element2), type(element3))
# <class 'str'> <class 'str'> <class 'str'>

 

ちなみに図の座標はテキトーなので、実際にidentify(8, 4)で’entry’が返るかは不明です。

identify(27, 3)とidentify(28, 5)に関しても同様。

 

それでは動きを見てみましょう。

マウスカーソルが現在どのパーツを指しているかを教えてくれるプログラムです。


import tkinter as tk


def update_label_text(event):
    x_coordinate = event.x
    y_coordinate = event.y

    element = spinbox.identify(x_coordinate, y_coordinate)

    label.config(text=f'Element: {element}')


root = tk.Tk()

label = tk.Label(root, text='Element: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

マウスを動かすたび、この関数が呼ばれます。

def update_label_text(event):
    x_coordinate = event.x
    y_coordinate = event.y

    element = spinbox.identify(x_coordinate, y_coordinate)

    label.config(text=f'Element: {element}')

 

まずは、現在のマウスカーソルの\(x\)座標と\(y\)座標を取得。

    x_coordinate = event.x
    y_coordinate = event.y

 

次に、取った座標をidentifyメソッドに渡し、座標に位置するパーツを文字列で受け取ります。

    element = spinbox.identify(x_coordinate, y_coordinate)

 

最後に、Labelにパーツを書き込んで終了です。

    label.config(text=f'Element: {element}')

 

実行結果です。

.bbox(index)

bboxメソッドは、Spinbox内で特定の文字を囲む長方形の位置とサイズを取得します。

対象の文字は引数のindexで指定します。

文字の番号を指定してあげてください。

 

すずか

文字を囲む長方形って何?

 

これが文字を囲む長方形、境界ボックス(Boundary box)です。

 

戻り値は4つの整数が入ったタプルです。

  • \(x\)座標
  • \(y\)座標
  • width(幅)
  • height(高さ)

座標は境界ボックスの左上のものです。

 

例えば次の図のような状況を考えます。

bboxメソッドに6を入れると、’r’の境界ボックスの情報が取れます。

‘r’の文字番号は6ですからね。

 

処理のイメージはこんな感じです。

bbox_information = spinbox.bbox(6)

print('x座標: ', bbox_information[0])
# x座標: 8

print('y座標: ', bbox_information[1])
# y座標: 3

print('width(幅): ', bbox_information[2])
# width(幅): 1

print('height(高さ): ', bbox_information[3])
# height(高さ): 2

 

毎度のことながら、図の座標はテキトーです。

図が綺麗になるように都合良く座標を設定したに過ぎません。

ですので、上のコードの数値が絶対正しいという保証はいたしかねます。

 

それでは実際に動きを見てましょう。

マウスカーソルに最も近い文字の境界ボックスの位置とサイズを表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    x_coordinate = event.x
    nearest_index = spinbox.index(f'@{x_coordinate}')
    char_bbox = spinbox.bbox(nearest_index)
    x, y, width, height = char_bbox

    label.config(text=f'x: {x}, y: {y}, width: {width}, height: {height}')


root = tk.Tk()

label = tk.Label(root, text='x: -, y: -, width: -, height: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

Spinbox上でマウスを動かすたびに、次の関数が呼ばれます。

def update_label_text(event):
    x_coordinate = event.x
    nearest_index = spinbox.index(f'@{x_coordinate}')
    x, y, width, height = spinbox.bbox(nearest_index)

    label.config(text=f'x: {x}, y: {y}, width: {width}, height: {height}')

 

まずはマウスカーソルの\(x\)座標を求めます。

    x_coordinate = event.x

 

次に、既に学んだindexメソッドに’@x’を引数で渡します。

指定された\(x\)座標に最も近い文字の番号を返すのでしたね。

    nearest_index = spinbox.index(f'@{x_coordinate}')

これで、マウスカーソルから一番近くの文字の番号が取れます。

 

ここでbboxメソッドの登場です。

文字の境界ボックスの位置・サイズ情報をタプルで受け取ります。

    char_bbox = spinbox.bbox(nearest_index)

 

タプル内の4つの整数を取り出します。

    x, y, width, height = char_bbox

 

最後はLabelに\(x, y\)座標・幅・高さを書きます。

    label.config(text=f'x: {x}, y: {y}, width: {width}, height: {height}')

‘x: 3, y: 1, width: 8, height: 2’の様に表示されます。

 

実行結果です。

やはり’i’や’t’、’r’はwidth(幅)が狭いですね。

.selection_adjust(index)

selection_adjustメソッドは、Spinbox内で挿入カーソルから指定の位置まで文字列を選択状態にします。

このメソッドは、Spinboxにフォーカスが当たっていないと発動しません。

 

例えば、Spinboxが次のような状態だとしましょう。

 

ここで、selection_adjust(6)を実行します。

spinbox.selection_adjust(6)

 

すると、挿入カーソルの位置から6の仕切り(引数で指定)まで文字列を選択状態にします。

 

実際に動きを確認してみましょう。

挿入カーソルを設定すると、’e’と’r’の間まで文字を選択状態にするプログラムです。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.focus_set()
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', lambda e: spinbox.selection_adjust(6))
spinbox.pack()

root.mainloop()

 

selection_adjustメソッドはSpinboxにフォーカスがないと動かないので、事前に自動でフォーカスを設定しておきます。

spinbox.focus_set()

起動直後に手動でフォーカスを当てる場合、この処理は不要です。

 

マウスカーソルが動くたびに、spinbox.selection_adjust(6)を実行します。

spinbox.bind('<Motion>', lambda e: spinbox.selection_adjust(6))

 

lambda式に慣れていない方は、以下のコードと等価だと思ってください。

def select_text(event):
    spinbox.selection_adjust(6)

spinbox.bind('<Motion>', select_text)

 

実行結果です。

.selection_from(index)・.selection_to(index)

selection_fromメソッドとselection_toメソッドは、セットで用いてSpinbox内の文字列を選択状態にします。

Spinboxにフォーカスを当てて使いましょう。

 

selection_fromメソッドは、文字列の選択開始位置を指定します。

selection_toメソッドは、文字列の選択終了位置を指定します。

 

例えば、’n’から’e’までの文字列を選択状態にしたい場合。

 

下記のコードを書けばよさそうですね。

spinbox.selection_from(3)
spinbox.selection_to(6)

 

実践してみましょう。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.focus_set()
spinbox.insert(0, 'Tkinter')
spinbox.selection_from(3)
spinbox.selection_to(6)
spinbox.pack()

root.mainloop()

実行結果です。

.selection_range(start, end)

selection_rangeメソッドはSpinbox内の文字列を選択状態にします。

Spinboxにフォーカスを当てて使いましょう。

 

引数は次の2つです。

  • start: 文字の選択を開始する位置
  • end: 文字の選択を終了する位置

 

すずか

なんかどっかで聞いたなぁ。

 

そうです。

先程のselection_fromメソッドとselection_toメソッドの合わせ技です。

 

例えば、’n’から’e’までの文字列を選択状態にしたい場合。

 

下記のコードを書けばOKです。

spinbox.selection_range(3, 6)

 

実践してみましょう。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.focus_set()
spinbox.insert(0, 'Tkinter')
spinbox.selection_range(3, 6)
spinbox.pack()

root.mainloop()

 

実行結果です。

selection_fromメソッドとselection_toメソッドのときと同じですね。

.selection_clear()

selection_clearメソッドはSpinbox内の文字列の選択を解除します。

 

実演してみましょう。

Enterを押すと、文字列の選択を解除するプログラムです。


import tkinter as tk


root = tk.Tk()

spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Return>', lambda e: spinbox.selection_clear())
spinbox.pack()

root.mainloop()

 

Enter押下でselection_clearメソッドが呼ばれます。

spinbox.bind('<Return>', lambda e: spinbox.selection_clear())

 

lambda式に慣れていない方は、以下のコードと等価だと思ってください。

def deselect_text(event):
    spinbox.selection_clear()

spinbox.bind('<Return>', deselect_text)

 

実行結果です。

 

普通にクリックで文字列の選択解除をしているように思えますが、きちんとEnterを押していますよ。

.selection_present()

selection_presentメソッドは、Spinbox内で文字列が選択されているかを教えてくれます

選択中であればTrueを、選択されていなければFalseを返します。

 

実演してみましょう。

文字列が選択中か否かを、Labelに表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    if spinbox.selection_present():
        label_text = 'Text is selected.'
    else:
        label_text = 'No text is selected.'

    label.config(text=label_text)


root = tk.Tk()

label = tk.Label(root, text='-')
label.pack()
spinbox = tk.Spinbox(root)
spinbox.insert(0, 'Tkinter')
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

マウスカーソルが動くたびに、この関数が実行されます。

def update_label_text(event):
    if spinbox.selection_present():
        label_text = 'Text is selected.'
    else:
        label_text = 'No text is selected.'

    label.config(text=label_text)

 

もし文字列が選択状態なら、selection_presentメソッドはTrueを返し、このif文に入ります。

    if spinbox.selection_present():
        label_text = 'Text is selected.'

label_text変数に’Text is selected.’を入れます。

「文字列が選択されている。」という意味です。

 

もし文字列が選択されていなければ、selection_presentメソッドはFalseを返します。

先程のif文には入らず、行き先はこちらのelse文。

    else:
        label_text = 'No text is selected.'

label_text変数に’No text is selected.’を入れます。

「文字列が選択されていない。」という意味です。

 

最後に、Labelを更新します。

label_textには、必ず’Text is selected.’か’No text is selected.’のどちらかが入っています。

    label.config(text=label_text)

 

実行結果です。

.selection_element(element=None)

selection_elementメソッドは、Spinbox右横のボタンをへこませます。

上下のボタンどちらを対象とするかは引数で決められます。

 

引数は次の3つから選択可能です。

 

また、selection_elementメソッドは引数を指定しなくても問題ありません。

その場合の動きも後ほど解説しますね。

‘buttonup’

‘buttonup’をselection_elementメソッドで使用すると、Spinbxの上ボタンをへこませます。

 

参考までに、私の環境ではこんな感じです。

上のSpinboxの上ボタンがうっすらと暗い色になっています。

普通に上ボタンをクリックすると、マウスをリリースするまでずっとこの色です。

 

確認できるようにコードを載せました。

比較のためにSpinboxを2種類用意し、selection_elementメソッドは上のSpinboxだけに適用させます。


import tkinter as tk


root = tk.Tk()

spinbox1 = tk.Spinbox(root)
spinbox1.focus_set()
spinbox1.selection_element('buttonup')
spinbox1.pack()

spinbox2 = tk.Spinbox(root)
spinbox2.pack()

root.mainloop()

 

実行結果です。

先程見た通りですね。

‘buttondown’

‘buttondown’をselection_elementメソッドで使用すると、Spinbxの下ボタンをへこませます。

 

参考までに、私の環境ではこんな感じです。

上のSpinboxの下ボタンがうっすらと暗い色になっています。

手動で下ボタンをクリックしても、リリースするまでこの状態です。

 

確認できるようにコードを載せました。

比較のためにSpinboxを2種類用意し、selection_elementメソッドは上のSpinboxだけに適用させます。


import tkinter as tk


root = tk.Tk()

spinbox1 = tk.Spinbox(root)
spinbox1.focus_set()
spinbox1.selection_element('buttondown')
spinbox1.pack()

spinbox2 = tk.Spinbox(root)
spinbox2.pack()

root.mainloop()

 

実行結果です。

よく見ないと違いがわからないですよね…。

‘none’

‘none’は、selection_elementメソッドで使用できますが、何も効果はありません。

 

一応コードと実行結果は載せておきます。

何の面白みもありませんが、興味のある方はどうぞ。

 


import tkinter as tk


root = tk.Tk()

spinbox1 = tk.Spinbox(root)
spinbox1.focus_set()
spinbox1.selection_element('none')
spinbox1.pack()

spinbox2 = tk.Spinbox(root)
spinbox2.pack()

root.mainloop()

 

実行結果です。

ただSpinboxを2つ並べただけに等しいです。

 

確かめずにはいられませんでした。

先生

すずか

…。

引数なし

selection_elementメソッドは、引数を渡さずに実行することが可能です。

現在押しているボタンの名称が文字列として返ってきます。

 

戻り値は3種類あります。

  • ‘buttonup’: 上ボタンを押す。
  • ‘buttondown’: 下ボタンを押す。
  • ‘none’: ボタンが押されていない。

引数と全く同じですね。

 

現在押しているボタンの名前を表示するプログラムです。


import tkinter as tk


def update_label_text(event):
    label_text = spinbox.selection_element()

    label.config(text=f'Element: {label_text}')


root = tk.Tk()

label = tk.Label(root, text='Element: -')
label.pack()

spinbox = tk.Spinbox(root)
spinbox.bind('<Motion>', update_label_text)
spinbox.pack()

root.mainloop()

 

実行結果をご覧ください。

.scan_mark(x)・.scan_dragto(x)

申し訳ございません。

この2メソッドに関しては、私も完全には理解できておりません。

しかし、分からないなりにも調査や実験を通して判明したことを書き留めます。

 

scan_mark及びscan_dragtoメソッドは、Spinbox内の文字列の水平方向のスクロールに関係するらしいです。

両メソッド共に引数は\(x\)座標です。

 

scan_markメソッドに\(x\)座標を渡すとその値を記憶する、らしいです。

 

scan_markの内部ソースのコメントに次の英文が書かれています。

Records x and the current view in the spinbox window;

used in conjunction with later scan dragto commands. Typically this command is associated with a mouse button press in the widget. It returns an empty string.

 

https://tedboy.github.io/python_stdlib/generated/generated/Tkinter.Spinbox.scan_mark.html

 

scan_dragtoメソッドに\(x\)座標を渡すと、事前にscan_markメソッドで記憶した\(x\)座標との差分を計算します。

そしてSpinbox内の文字列を、その差分の10倍の距離だけ左右にスクロールさせるようです。

 

scan_dragtoの内部ソースのコメントに次の英文が書かれています。

Compute the difference between the given x argument and the x argument to the last scan mark command

It then adjusts the view left or right by 10 times the difference in x-coordinates. This command is typically associated with mouse motion events in the widget, to produce the effect of dragging the spinbox at high speed through the window. The return value is an empty string.

 

https://tedboy.github.io/python_stdlib/generated/generated/Tkinter.Spinbox.scan_dragto.html

 

とはいうものの、残念ながらscan_markメソッドに関しては全く分かりませんでした。

というのも、私の環境では何度実験してもscan_dragtoメソッドとの連携が確認できなかったのです。

しかもscan_markメソッドを使わずにscan_dragtoメソッドだけでもスクロールが可能な模様。

環境が悪いのか、使い方を誤っているのか…。

 

取り敢えず分かったことをまとめます。

scan_dragtoを使うと、Spinboxの文字が長いときに、入力欄をホバーするだけで横スクロールができます。

 

実験してみたソースと結果を残します。

scan_dragtoメソッドを使ったSpinboxと、普通のSpinboxとの比較検証です。


import tkinter as tk


spinbox_text = 'いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑいもせす'

root = tk.Tk()

spinbox1 = tk.Spinbox(root)
spinbox1.insert(0, spinbox_text)
spinbox1.bind('<Motion>', lambda e: spinbox1.scan_dragto(e.x))
spinbox1.pack()

spinbox2 = tk.Spinbox(root)
spinbox2.insert(0, spinbox_text)
spinbox2.pack()

root.mainloop()

 

scan_dragtoメソッドによる文字列のスクロールを試します。

ということは、Spinbox内の文字列をスクロールできるほど長くする必要があります。

文字列は何でもいいのですが、偶然思い付いたのがいろは歌でした。

spinbox_text = 'いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑいもせす'

 

マウスカーソルが動くと同時に、spinbox1にscan_dragtoメソッドを実行します。

spinbox1.bind('<Motion>', lambda e: spinbox1.scan_dragto(e.x))

同メソッドの引数は\(x\)座標なので、カーソルの\(x\)座標であるe.xを渡します。

普通のSpinboxと比較したいので、spinbox2にはイベント設定はしてありません。

 

lambda式を初めて見る方は、次のコードと同じだと思ってください。

def scroll(event):
    x_coordinate = event.x
    spinbox1.scan_dragto(x_coordinate)

spinbox1.bind('<Motion>', scroll)

 

実行結果をご覧ください。

上がscan_dragtoメソッドを使ったSpinboxです。

 

私が理解しているのはここまでです。

scan_mark・scan_dragtoメソッドについて、何か情報をお持ちの方がいらっしゃいましたら、コメント欄で教えていただけたら幸いです。

まとめ

この記事では、TkinterSpinboxクラスが持つ多くのメソッドをご紹介しました。

 

本記事で取り上げたSpinboxのメソッドは次の通りです。

Spinboxクラスのメソッド

 

とはいえ、実際は後半のメソッドは使用頻度が高くないので、せめてこの3つだけでも覚えていただければ幸いです。

【厳選】Spinboxクラスのメソッド

 

今回はSpinboxのメソッドでしたが、いずれオプション一覧も記事にする予定です。

楽しみにしていてください。

 

最後までお付き合いいただきありがとうございました。

 

この記事に関するご意見や質問・感想などは、ご自由にコメント欄に投稿してください(コメント欄はこの記事の最下部です)。

※頂いたコメントは全て拝見し、真剣に回答させていただきます!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA