【Python tkinter】複数のボタンを配置したフレームにスクロールバーを設置する方法

2021.01.11 /

【Python tkinter】複数のボタンを配置したフレームにスクロールバーを設置する方法

本記事ではtkinterを用いたGUIアプリ開発で、複数のButton(ボタン)ウィジェットを配置したFrame(フレーム)ウィジェットにScrollbar(スクロールバー)ウィジェットを設置する方法を解説していきます。

フレームウィジェットはスクロールバーウィジェットに対応していないため、そのままではスクロールバーを付けることはできません。

フレームにどのようにしてスクロールバーを設置するのか見ていきましょう。

本記事を通して以下の知識を学べます。

学べる知識
  • ttkにおけるButton(ボタン)ウィジェットとスクロールバーを紐づける方法
  • エクスプローラーのようなGUI作成方法
  • カレントディレクトリのパスの取得方法
  • サブディレクトリ一覧情報の取得方法

Frame(フレーム)にスクロールバーを設置する方法

Scrollbar(スクロールバー)ウィジェットとは

Scrollbar(スクロールバー)ウィジェットは対象ウィジェットに対して、縦方向・横方向にスクロールできるバーを設置できます。

しかしスクロールバーに対応しているウィジェットは限られています。
Listbox(リストボックス)・Text(テキスト)・Canvas(キャンバス)・Entry(エントリー)のみが対応しています。

詳しいScrollbarウィジェットの使用方法は以下記事をご参照ください。

どのようにフレームにスクロールバーを設置するのか

上記の説明を見るとわかるように、フレームウィジェットはスクロールバーに対応していません。

では本記事のテーマである複数のボタンウィジェットを配置したフレームウィジェットに対してどのようにスクロールバーを設置するのか。

答えは、Canvasウィジェットにボタンウィジェットを配置したフレームウィジェットを配置し、Canvasウィジェットに対してスクロールバーを設置することです。

Canvasウィジェットはスクロールバーに対応しているので問題なく設置することができます。

詳しい方法は以下サンプルプログラムを使って解説していきます。

サンプルプログラム

プログラム内容

作成するプログラムの内容を以下に記します。

  • 現在のディレクトリ内のサブディレクトリ一覧を表示
  • ディレクトリ名はボタンウィジェット上に表示
  • サブディレクトリ一覧はスクロールバーに対応
  • エクスプローラーのような外観に仕上げる

ソースコード

サンプルプログラムのソースコードを以下に記します。

tkinter:GUIサンプルプログラム(ボタンにスクロールバーを追加)
tkinter:GUIサンプルプログラム(ボタンにスクロールバーを追加:hover)
import tkinter as tk
from tkinter import ttk
from tkinter import PhotoImage
import os
import sys
import pathlib

# ------------------------------------------------
### Canvas上のマウススクロール関数 ###
def mouse_y_scroll(event):
    if event.delta > 0:
        canvas.yview_scroll(-1, 'units')
    elif event.delta < 0:
        canvas.yview_scroll(1, 'units')
# ------------------------------------------------

def canvas_create(path):
    global canvas
    files_name = pathlib.Path(path)

    i = files_name.iterdir() # ファイル、フォルダのパスを格納
    k = files_name.iterdir() # ファイル、フォルダのパスを格納

    file_num = 0
    for file in (f.name for f in k if f.is_dir()):
        file_num = file_num + 1

    # キャンバスの作成
    canvas = tk.Canvas(frame, width=280,bg="white", bd=0, height=200, highlightthickness=0)
    # キャンバスを描画
    canvas.grid(row=0, column=0)

    # 縦スクロールバーの作成 #
    scrollbar = tk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview)
    scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))

    # スクロールバーをCanvasに関連付け
    canvas["yscrollcommand"] = scrollbar.set

    # Canvasの位置の初期化
    canvas.yview_moveto(0)

    size_y = file_num * 24
    canvas.config(scrollregion=(0,0,0,size_y)) #スクロール範囲

    frame_canvas = tk.Frame(canvas)  #frameの中にcanvas,bar用のframeを作成
    # Frame Widgetを Canvas Widget上に配置
    canvas.create_window((0,0), window=frame_canvas, anchor=tk.NW, width=canvas.cget('width'))
    canvas.bind("", mouse_y_scroll)

    for f in (files.name for files in i if files.is_dir()):
        icon_folder = PhotoImage(file=pathlib.Path(sys.argv[0]).parent.absolute() / "folder_icon_16.png", width=12, height=12)
        button_folder = ttk.Button(frame_canvas, text=f, style="White.TButton", width=50, compound="left", image=icon_folder)
        button_folder.photo = icon_folder
        button_folder.bind("", mouse_y_scroll)
        button_folder.pack()

# rootメインウィンドウの設定
root = tk.Tk()
root.title("Directory Display")
root.geometry("500x250")

path = os.getcwd()

# ボタンのスタイル
style = ttk.Style()
style.theme_use('default')
style.configure("White.TButton", background="white", anchor="w", borderwidth=0)

# メインフレームの作成と設置
frame = tk.Frame(root)
frame.pack(padx=20,pady=10)

canvas_create(path)

root.mainloop()

サンプルプログラム解説

フレームにスクロールバーを対応させるまでの流れ

Frameウィジェット、Canvasウィジェット、Scrollbarウィジェット、Buttonウィジェットは以下の流れで作成・配置しています。

  1. メインウィンドウ(root)の作成・設定
  2. メインフレーム(frame)を作成・rootへ配置
  3. キャンバス(canvas)を作成・frameへ配置
  4. スクロールバー(scrollbar)を作成・canvasに関連付け
  5. フレーム(frame_canvas)を作成・canvasへ配置
  6. ボタン(button_folder)を作成・frame_canvasへ配置

現在のディレクトリパスの取得

現在のディレクトリの絶対パス(pyファイルの保存場所)を取得するために、os.getcwd()メソッドを使用しています。

path = os.getcwd()

サブディレクトリ一覧の取得

現在のディレクトリ内のサブディレクトリ一覧はpathlibモジュールのPathクラスであるiterdir()メソッドを使用します。

i = files.iterdir()

サブディレクトリ一覧からディレクトリだけ取り出すためにis_dir()メソッドを使用します。

サンプルプログラム内では繰り返しfor文を使い、ディレクトリ名をボタンウィジェットに指定しています。

for f in (files.name for files in i if files.is_dir()):

Canvasの高さ指定

スクロールバーはフレームではなく、実際はCavasウィジェットに設置され、Canvasウィジェットの高さで上下スクロールされます。

そのためCavasウィジェットのスクロール範囲をフレームの高さに合わせる必要があります。

size_y = file_num * 18

canvas_left.config(scrollregion=(0,0,0,size_y))

サンプルプログラムではボタンの高さ×ボタンの数分の高さをスクロールバーのスクロール範囲としています。

ボタンスタイルの変更

エクスプローラーのような外観となるようにボタンウィジェットのスタイルを変更しています。

style = ttk.Style()

style.theme_use('default')

style.configure("White.TButton", background="white", anchor="w", borderwidth=0)

ttkモジュールにおけるボタンウィジェットのスタイル変更に関しては以下記事をご参照ください。

【ttkモジュール】ボタンウィジェットのスタイル変更について

まとめ

本記事「複数のボタンを配置したフレームにスクロールバーを設置する方法」はいかがでしたか。

フレームがスクロールバーに対応していないために、ややこしい方法でスクロールバーをフレームに設置したように見せかけました。

この記事で紹介している方法がベストな方法かは私もわかりません。ですが現段階ではこの方法しかボタンウィジェットをスクロールさせる方法は思いつかないです。

ほかに良い方法がわかりましたら随時記事を更新していきます。