Python GUIプログラミング tkinter 自動で消えるスクロールバー

 こんにちは。見に来てくださって、ありがとうございます。石川さんです。

 今日は、先日書いた記事のスクロールバーが必要のないときに消える方法について書きました。

出来上がりイメージ

項目の位置によってスクロールバーが出たり消えたりする

ソースコード

ソースコードは以下の通りです。

import tkinter as tk


class AutoScrollbar(tk.Scrollbar):  # pylint: disable=too-many-ancestors
    ''' 必要に応じて自動で消えるスクロールバー
        grid と pack geometry manager で利用可能です'''
    def __init__(self, master=None, **kw):
        super().__init__(master, **kw)
        self.geo_mgr = self.key_word = None

    def set(self, first, last):
        self.geo_mgr = self.geo_mgr if self.geo_mgr else self.winfo_manager()
        if float(first) <= 0.0 and float(last) >= 1.0:
            if self.geo_mgr == "grid":
                self.grid_remove()
            elif self.geo_mgr == "pack":
                self.pack_forget()
        else:
            if self.geo_mgr == "grid":
                self.grid()
            elif self.geo_mgr == "pack":
                self.pack(**self.key_word)
        super().set(first, last)

    def pack(self, **kw):  # pylint: disable=arguments-differ
        ''' pack geometry managerのキーワード引数を保持 '''
        self.key_word = kw
        super().pack(**kw)

    def place(self, **kw):  # pylint: disable=no-self-use
        ''' place geometry manager を使ったときのための警告 '''
        raise tk.TclError('placeは使えません')


class App(tk.Tk):
    ''' メインアプリケーションクラス '''
    def __init__(self):
        super().__init__()
        self.title("Canvas move test")
        self.geometry("400x200")

        # gridバージョン
        self.canvas = can = tk.Canvas(self, background="white")
        sbx = AutoScrollbar(self, orient=tk.HORIZONTAL, command=can.xview)
        sby = AutoScrollbar(self, orient=tk.VERTICAL, command=can.yview)
        can.config(xscrollcommand=sbx.set, yscrollcommand=sby.set)
        self.info = tk.Label(self)
        can.grid(row=0, column=0, sticky=tk.NSEW)
        sbx.grid(row=1, column=0, sticky=tk.EW)
        sby.grid(row=0, column=1, sticky=tk.NS)
        self.info.grid(row=2, columnspan=2)
#        # packバージョン
#        self.canvas = can = tk.Canvas(self, background="white")
#        sbx = AutoScrollbar(can, orient=tk.HORIZONTAL, command=can.xview)
#        sby = AutoScrollbar(can, orient=tk.VERTICAL, command=can.yview)
#        can.config(xscrollcommand=sbx.set, yscrollcommand=sby.set)
#        self.info = tk.Label(self)
#        can.grid(row=0, column=0, sticky=tk.NSEW)
#        sbx.pack(side=tk.BOTTOM, fill=tk.X)
#        sby.pack(sid=tk.RIGHT, fill=tk.Y)
#        self.info.grid(row=1, columnspan=2)

        self.start = self.item = None
        can.bind("<Motion>", self.move)
        can.bind("<ButtonPress>", self.button_press)
        can.bind("<ButtonRelease>", self.button_release)
        can.bind("<MouseWheel>", self.mouse_wheel)
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)
        self.bind("<Configure>", self.resize)

        can.create_rectangle(40, 40, 60, 60)

    def move(self, event):
        ''' マウスが動いたときの処理 '''
        x = self.canvas.canvasx(event.x)  # pylint: disable=invalid-name
        y = self.canvas.canvasy(event.y)  # pylint: disable=invalid-name
        text = f"mouse position = ({event.x}, {event.y}) ({x},{y})"
        self.info.configure(text=text)
        if self.start and self.item:
            original_x, original_y = self.start
            self.canvas.move(self.item, x - original_x, y - original_y)
            self.start = x, y
            self.resize(event)

        elif event.state == 256:  # Button1
            self.canvas.scan_dragto(event.x, event.y, gain=1)

    def button_press(self, event):
        ''' マウスのボタンを押されたときの処理 '''
        x = self.canvas.canvasx(event.x)  # pylint: disable=invalid-name
        y = self.canvas.canvasy(event.y)  # pylint: disable=invalid-name
        self.start = x, y
        self.item = self.canvas.find_overlapping(x-10, y-10, x+10, y+10)
        if self.item:
            self.item = self.item[0]
        else:
            self.canvas.scan_mark(event.x, event.y)
        if event.num == 3:
            self.item = self.canvas.create_rectangle(x-10, y-10, x+10, y+10)

    def button_release(self, event):  # pylint: disable=unused-argument
        ''' ボタンが離されたときの処理 '''
        self.start = None

    def resize(self, event):  # pylint: disable=unused-argument
        ''' ウィンドウサイズが変わった時の処理 '''
        region = self.canvas.bbox(tk.ALL)
        if region[0] > 0:
            region = (0, *region[1:])
        if region[1] > 0:
            region = (region[0], 0, *region[2:])
        self.canvas.configure(scrollregion=region)

    def mouse_wheel(self, event):
        ''' ホイールが操作されたときの処理 '''
        if event.state == 5:  # Shift|Control
            self.canvas.scan_mark(event.x, event.y)
            self.canvas.scan_dragto(event.x + event.delta // 120,
                                    event.y, gain=10)
        elif event.state == 4:  # Control
            scale = 1 + event.delta / 1200
            self.canvas.scale(tk.ALL, event.x, event.y, scale, scale)
        elif event.state == 1:  # Shift
            pass
        elif event.state == 0:  # None
            self.canvas.scan_mark(event.x, event.y)
            self.canvas.scan_dragto(event.x,
                                    event.y + event.delta // 120, gain=10)
        self.resize(event)


def main():
    ''' メインプログラム '''
    app = App()
    app.mainloop()


if __name__ == "__main__":
    main()

説明

 今回は、Scrollbarを継承して、AutoScrollbarを作成しました。ま、基本的には、ここで記載のあった方法を踏襲しています。違いはpackでも利用できるようにしたことくらいでしょうか。ポイントはScrollbarのsetが呼び出されたときに、必要がなければ、grid_remove(pack_forget)を使って見えなくする、というところでしょうか。あと、packでも利用可能にするために、winfo_managerでセットされているgeometry managerを取得してインスタンス変数に保持するようにしました。packでの利用方法は、コメントアウトしてあるところです。
 その他の部分は前回とおんなじです、、、と思いましたが、一部、pylintでチェックして規約違反やら警告やらリファクタと言われたところを修正いたしました。スッキリしました!

まとめ

 スクロールバーが自動で消えるようになりました。

コメントを残す

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


The reCAPTCHA verification period has expired. Please reload the page.