Tkinterでマウススクロール可能なフレームを作成するには

## Tkinterの情報、多いようで少ないし、Class化に至っては魔境の領域か
Tkinter。PythonベースのGUIとしてあれこれ実装したいならば必須なのかなと感じます。
ほかはざっと見た感じ、単純な操作のみの簡易版か、ゲームを作る的なベースコンセプト含有のもの。
ベタなものでいいだろうということでTkinterをチョイスし、作りたいものがあったため、かれこれ二ヶ月くらい大格闘して一つGUIアプリを作りました。

開始数週間時点でTkinterの知見が深まったことにより大部分が作り直しになったり、勉強しながらなので当然遅かったりするわけですが、ある程度区切りのつくものが作れたと思います。
よくある「電卓を作ってみましょう」的な、勉強のための車輪の再発明をしている余裕はないので、ハナから実践的なツールである必要があったのです。デンジャラスな挑戦でしたが、めげずに達成できました。

2021年11月現在、Tkinterに関しては、動画やWebレクチャー自体はそれなりにあります。ところが、第一にベタ書きのものが多いです。
それこそ先述の、勉強のためのお気楽なツール作り程度でしたら構いませんが、使い回しできることを加味したり、長大になっても可読性を落とさないためには、Class化は必須。
もちろんClass化で解説してあるものも多々あります。非常にありがたい限り。
ところが、rootの継承あたりの挙動までストンと落ちるような明瞭な解説をしているものは皆無で、ベタ書きの知識となかなか混在させられないのが難点。
使いまわそうと思うと、頭から湯気を出しながらなんとかできるかできないかという感じ。

これはオブジェクト指向に明るい技術者でしたら鼻で笑うような内容なんでしょう。
もちろん今回の開発でオブジェクト指向のオの字くらいはわかったような気はしますが、＊argsとかですぐ湯気が出てしまうのでまだまだです。
Classや継承そのものを解説した動画は多いんですが、なんというか＊argsなんかもそうですが、Tkinterの使い回しの理解ができるほどの実践的なものは少なく、犬種や車に例えたらそりゃ簡単ですが、もうちょっと実践的というかTkinterのようなライブラリの継承を用いた解説がほしいところですね。

Tkinterの情報、多いようで少ないし、Class化に至っては魔境だというのが第一の感想です。
長くなりそうなので感想は置いておき、命題である、詰まる人が非常に多そうな挙動について、**私はこの挙動について真に驚くべき証明を発見したが、ここに記すには余白が広すぎるので記します**。

## Tkinter、スクロールの実装と挙動なんとかならんのか？
キリ番文化くらいからパソコンに触れている身としては、スクロールは昔から当たり前にできるものだったんですよね。母国語のようにその挙動は体に染み付いているんです。
ブラウザなどの枠内に長そうなコンテンツが収まっていれば右にスクロールバーがあって、マウスホイールをグリグリするとその部分だけが上下するんですよ。それがスクロールってやつです。
ただこれを作ろうと思うと、少なくともTkinterでは、非常に面倒なステップを踏まなければなりません。ブラウザなんかは座標周りの処理など、とても難しいことをやっていたんですね。

いろいろ問題点を考えます。

### スクロールバーをわざわざ作らなければいけない＆デフォでスクロールできるウィジェット
Frameで枠を作って、そのなかにいろいろ配置するのがTkinterにおけるセオリー、ファーストステップだと思いますが、Frameはスクロールバーを配置できません。
そもそもスクロールバー自体がウィジェットになっており、非常に難解な方式で配置せざるを得ません。私はこのあたりのルールをほぼ理解できておらず、コピペで済ませています。
どうやらCanvasだったらスクロールバーを配置できるようで、調べるとそのように記述解説しているレクチャーが多いですね。
いや、どう考えても、Frameのconfigureでスクロールの有無やバーの有無やバーの幅などを簡単に設定できるべきで、わざわざCanvasを迂回する意味がわかりません。Canvasはほかのウィジェットと異なり実装や配置が独特で、チープな図形をぐりぐり動かしたいならちゃんと勉強すべきでしょうけど、はっきり言ってGUIということを考えると習熟コストが悪すぎますね。
おそらくですがグラフなどは別のものを頼ることになるでしょうし、グラフィカルなコンテンツがいいなら他のGUIアプリ、ゲーム用開発環境のほうがいいのではと思います。
餅は餅屋。

一方で実はデフォでスクロール機能が内包されているウィジェットもあり、代表的なのはTextでしょう。
指定した高さ以上に文字を入力すると自動的にマウススクロールが可能になります。

・・・それをなぜFrameで導入しない！？

## Frame→Canvas→Frameでスクロールフレームを実装・・・まではいいのだが
まずはこちらの方の記事です。
https://qiita.com/shinno1993/items/3ea14ffd7f17d8214961
序盤非常に役立ちましたが、最終的に完成したGUI内では設計リファクタリングにより、今回紹介する改造版1種類のみの使用となりました。とにかく敬意を表したいと思います。
また、改造部分についてはこちらの記事が参考になりました。同じく敬礼！
https://qiita.com/hatorijobs/items/9cad567a01511166e95b

上のほうの記事では、Frame上に物を配置して、スクロールすることを可能にしています。
が、幅の調整などが非常にわかりづらく、モジュール化して利用させてもらう際に非常に難儀しました。

### ScrollableFrameの幅と高さの調整　横のスクロールは不要な場合
```python
from scrollframe import *
sf = ScrollableFrame(root,bar_x = False)
sf.grid(column=0,row=0,ipadx=500,ipady=500)
```

こんな感じで、gridの場合はそのオプションにてipadx,ipadyを指定することで幅と高さを変えられます。内側がグイーンと広がってくれるイメージです。
また、bar_x = Falseにすることで横のスクロールを消してくれます。横にスクロールすることは大概ないですからね。

### ScrollableFrameの欠点
なんといっても、マウススクロールに対応していないことでしょう。
スクロールバーの上でマウスホイールをぐりぐりするか、スクロールバーをマウスドラッグで掴むかしかありません。
そのため最初は、スクロールバーの幅を30pxに広げた改造版ScrollableFrameを使っていましたが、とにかくスクロールが面倒臭い。
どう考えてもフレーム上でマウスホイールをぐりぐりしたらスクロールできるようにすべきで、今回頑張って改造しました。

あとそう、上のコードでsfという形でScrollableFrameをインスタンス化していますが、実際にボタンなどのウィジェットを配置するときは、
sf.scrollable_frameという風に指定しなければ怒られます。

## ScrollableFrameのFrame上でマウスホイールをぐりぐりしてスクロールできるようにする
いよいよ本題です。
個人的な備忘録なので、部分部分のコードのみです。

```python
#scrollframe.py内initに追加
self.scrollable_frame.bind("MouseWheel",self.mouse_y_scroll)
#scrollframe.py内defを追加
def mouse_y_scroll(self, event):
    if event.delta > 0:
        self.canvas.yview_scroll(-1, 'units')
    elif event.delta < 0:
        self.canvas.yview_scroll(1, 'units')

#インスタンス化したフレームに配置するボタンなどのbindとして
btn.bind("<MouseWheel>", sf.mouse_y_scroll)
```
ScrollableFrameをいじらせていただき、あとは配置の際にフレームではなくそれぞれのボタンやLabelやEntryなどにbindする形。
実装はほんの数行追加するだけでしたが、先に述べたとおりオブジェクト指向思考がまだまだなので、かなり難儀しました。
ともあれこれで、ボタンなどを配置したFrameをぐりぐりとスクロールすることが可能になりました。

とにかくこれの元となった記事のエンジニアさんには感謝感激でございます。
TkinterでGUI開発をされているどなたかのお役に立てれば幸いであります。