質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

Q&A

解決済

1回答

2439閲覧

Python3 Tkinter 時間入力やテンキーなどの専用画面をウィジェットのすぐ下に表示したい

person

総合スコア223

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Tkinter

Tkinterは、GUIツールキットである“Tk”をPythonから利用できるようにした標準ライブラリである。

0グッド

0クリップ

投稿2022/04/07 05:11

編集2022/04/07 05:46

Tkinterで時間入力やテンキーなどの専用入力画面を出したいです。

例えば下のコードで、
Treeviewの中から時間をダブルクリックすると
その時間を変更できるようにしたいです。

ダブルクリックしたときに添付画像のように、
該当データのすぐ下に表示したいのですが、
Treeviewや他のウィジェットが既に表示したいエリアへ配置済みの場合は
Toplevel()を使う以外ないでしょうか?

フレームの場合はあらかじめ、フレームを配置しておく必要ありますか?

また、画像のようにウィンドウからはみ出す可能性もありますが、
フレームの場合はこういうことは当然できないでしょうか?

Python

1import tkinter as tk 2from tkinter import ttk 3 4def on_tree(e): 5 tree = e.widget 6 selected = tree.selection() 7 clickarea = tree.identify_region(e.x, e.y) 8 9 if len(selected) == 1 and clickarea == "cell": 10 # 列の取得 11 x = tree.winfo_pointerx() - tree.winfo_rootx() 12 select_column_str = tree.identify_column(x) 13 select_column_int = int(select_column_str[1:]) 14 15 # 時刻列ダブルクリックで専用入力画面出したい 16 if select_column_int == 2: 17 timestr = tree.item(selected[0])["values"][select_column_int - 1] 18 print(timestr) 19 20 21root = tk.Tk() 22 23tree = ttk.Treeview(root, selectmode="browse") 24tree.pack() 25 26# headings 27columns = 2 28tree["columns"] = tuple(range(columns)) 29tree["show"] = "headings" 30tree.heading(0, text="No.") 31tree.heading(1, text="Time[hh:mm]") 32 33# data 34tree.insert("", "end", values=("1", "00:00")) 35tree.insert("", "end", values=("2", "01:11")) 36tree.insert("", "end", values=("3", "02:22")) 37tree.insert("", "end", values=("4", "03:33")) 38tree.insert("", "end", values=("5", "04:44")) 39 40tree.bind("<Double-Button-1>", on_tree) 41 42root.mainloop()

イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

方法としては2通り

  • Toplevel でポップアップ表示するか (overrideredirectでタイトルバーは非表示)
  • Frame を place メソッドで座標位置指定で配置できます。

(重ね合わせの順序の制御が必要な場合は、tkraiseで上に持ってくる。place_forgetで非表示に。)

ウィンドウ外にはみ出して表示したい場合は、
FrameではなくToplevel にする必要があります。
セル内の表示範囲でインライン編集の場合は、Frame といった使い分けでしょうか。

Toplevelの場合は、フラットフォーム毎に挙動が異なる場合がある点も注意ですが、
ttk.Combobox のポップダウン等ではToplevel が使われています。

どちらの場合でも、画面もしくはウィンドウからはみ出る場合は、
表示座標を工夫しなければなりません。

Toplevel -> (デスクトップ画面全体の)スクリーンサイズを判別して、見切れる場合は調整
Frame -> ウィンドウサイズを判別して、見切れる場合は調整


追記: ttk.Treeview 拡張 EditableTreeview での実装例。該当箇所
https://github.com/alejandroautalan/pygubu/blob/c0d77178cabb16c3e3cf8d420c071e53787063ad/pygubu/widgets/editabletreeview.py#L139

place/place_forget での表示非表示。この実装の場合は、inline表示なので bbox メソッドでセル範囲の座標を求めています。

追記2: ttk.combobox でのポップダウン表示 ソースコード該当箇所(TCL言語)
https://github.com/nomad-software/tcltk/blob/12310ca54634516f8f2210dd65e810b6cb3ef233/dist/library/ttk/combobox.tcl#L366

投稿2022/04/07 06:10

編集2022/04/07 06:40
teamikl

総合スコア8664

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

person

2022/04/08 00:50 編集

特にどちらでもよければToplevelでやった方がいいんでしょうかね。 Toplevelを表示するy座標はどのように計算すればいいでしょうか? x座標はtree.winfo_rootx() + tree.column(0)["width"]にしようと思っています。多少右にずれますが、まあぴったりじゃなくてもいいので。
teamikl

2022/04/08 01:05

> 特にどちらでもよければToplevelでやった方がいいんでしょうかね。 コーディング上の注意としては、相対座標と絶対座標の違いでしょうか。 編集欄のウィジェット自体は Frame 上もしくは単品の部品として実装しておき、 配置の際に Toplevel か place どちらの方法でも使えるようにしておきます。 セルの枠内に収まる形で配置するなら place の方が簡単です。bbox で得た値をそのまま渡すだけで良い為。 それ以外なら、基本 Toplevel でいいと思います。 >Toplevelを表示する位置はどのように計算すればいいでしょうか。 見切れる際に、どのように補正するかで変わってきますが、一例を上げると IF 表示座標 + 表示するウィジェットの幅 > 画面のサイズ  表示座標 = 画面サイズ - 表示するウィジェットの幅 Tcl 言語ですが、追記2 のソースコードが Combobox のポップダウンでの例です。 ポップダウンは下方向に表示しますが、見切れる場合は上に表示してます。 横方向の幅はコンボボックスと同じ幅な為、考慮されてません。 ソースコードの読み方 winfo ~~ 型のメソッドは winfo_~~ で同名のメソッド呼び出し可能です。 変数 x y w h $cb ... コンボボックスの座標(x,y)とサイズ(w,h) オプション postoffset ... オプションで、与えられた任意の値を x y w h に加算する。(位置補正 winfo reqheight ... 表示するウィジェットの縦幅 winfo screenheight ... 画面の縦幅 treeview に適応すると、 x y w h はセルの座標とサイズ。bbox メソッドで求まります。参考: 追記1 相対座標なので、winfo rootx, rooty を加算し、画面上の座標を計算。
person

2022/04/08 02:35 編集

コメントを都度編集してました、申し訳ありません。 > 記1 > 相対座標なので、winfo rootx, rooty を加算し、画面上の座標を計算。 これはx座標求めるときに使うということでいいですよね? 見切れる場合は、screenwidth() - winfo_width()で画面右ぴったりに出せればいいかなと思っています。 y座標求めるときってx座標を求めるときとは違う求め方だと思うのですが、これも同様なのでしょうか。 クリックしたレコードの座標が求められるのがいいのですが、無理な場合は tree.winfo_pointery() で近い座標をとるべきでしょうか。
teamikl

2022/04/08 03:59

x, y, どちらも同様です。 質問コード内にも絶対座標→相対座標の変換がありますが、 x = tree.winfo_pointerx() - tree.winfo_rootx() は、イベントハンドラ内なので この x は e.x と同じです。 Toplevel に表示する座標はこの逆のケースで、bbox で得られる Treeview ウィジェット内の座標に Treeview の絶対座標 rootx, rooty を足す事になります。 # 選択されたセルを起点にする場合 x, y, w, h = tree.bbox(selected[0], select_column_str) # Treeview内のcellの座標+サイズ x += tree.winfo_rootx() y += tree.winfo_rooty() マウスのクリックされた位置を起点にする場合は、pointerx, pointey は絶対座標なので、 そのまま使えます。(相対座標 + 補正) e.x + rootx == (絶対座標) pointerx です この x, y を起点に、 ポップアップで表示するダイアログのサイズ (winfo_reqwidth, winfo_reqheight) を求め x + width と y + height がそれぞれ 画面のサイズ (winfo_screenwidth, winfo_screenheight) を超えないかをチェックします。 もし超えてしまう場合は、逆向きに表示や、画面端に収まる座標にずらす等、 お好みの方法で表示位置を調整します。 後、表示するウィジェットは、イベントハンドラ内で毎回生成はせずに、 予め1個のみ生成しておき、表示非表示を切り替える方式で実装します。 (呼び出し毎に毎回作る方法では効率はよくなく、ミスが有った時にメモリリークが起きやすくなります)
person

2022/04/08 05:46

> # 選択されたセルを起点にする場合 > x, y, w, h = tree.bbox(selected[0], select_column_str) # Treeview内のcellの座標+サイズ > x += tree.winfo_rootx() > y += tree.winfo_rooty() ありがとうございます。x座標はこれでいいのですが、Toplevel()で元の時間が隠れてしまうのを避けたいのですが、座標に補正用の適当な数字足す以外ないですかね?
person

2022/04/08 06:18

追加 > 後、表示するウィジェットは、イベントハンドラ内で毎回生成はせずに、 > 予め1個のみ生成しておき、表示非表示を切り替える方式で実装します。 > (呼び出し毎に毎回作る方法では効率はよくなく、ミスが有った時にメモリリークが起きやすくなります) Toplevelでもそれは可能でしょうか、 Toplevel()呼ぶだけでウィンドウが出てくるので、都度生成になりそうですが、これはミスらないようにするしかないのでしょうか。
teamikl

2022/04/08 07:01 編集

> 元の時間が隠れてしまうのを避けたい、補正用の適当な数字足す 「適当な数値」ではなく、bbox でクリックされたセルのサイズも所得できるので、 正確に被らないように範囲を調整できます。 > Toplevelでもそれは可能でしょうか、 可能です。ウィンドウは withdraw/deiconify で非表示・表示 「ミスらないようにする」は、一時的な回避策としてはそのとおりですが、 想定外の例外に対応するには、例外をすべて補足しなければならなく、 そういったコードは、デバッグに必要なエラー情報も隠してしまうので、 注意点が多く、コードの保守が難しくなりやすい傾向にあります。 都度生成する場合の他の対応策は、Toplevel や ウィジェット生成時の name オプションを付けて ウィジェットを再利用する形でメモリ抑制は出来ますが、全てのウィジェトに name を付けなければならない為、 コードが冗長になります。(name付け忘れがメモリーリークに繋がるが、確認が困難) 予め生成しておく方法では、座標指定・非表示/表示切り替えのみで済みます。 やりやすい実装方法で良いと思いますが、実装方法のお勧め優先度としては (1) 予め作成しておき、表示・非表示 (2) 毎回生成するが、生成時にname指定する (3) 毎回生成する
teamikl

2022/04/08 14:41 編集

>>座標に補正用の適当な数字足す以外ないですかね? >「適当な数値」ではなく、bbox でクリックされたセルのサイズも所得できるので、 正確に被らないように範囲を調整できます。 x, y, w, h = tree.bbox(selected[0], select_column_str) # Treeview内のcellの座標+サイズ x += tree.winfo_rootx() y += tree.winfo_rooty() + h # セルの縦幅を足して、被るのを回避 if y + ポップアップの縦幅 > 画面サイズ: # 画面下で見切れる場合は、上に表示  y -= (h + ポップアップの縦幅) ---- Toplevel / place との実装方法の対比で、 Toplevel の場合は他に、ポップアップが閉じないようにする フォーカスが外れた時に非表示等、実用するには細かな調整が必要になりそうです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問