今日も見に来てくださって、ありがとうございます。石川さんです。
前回、アウトラインの案として、キャンバスの中に、create_window()でキャンバスを追加する、というやり方で実装してみましたが、Toplevel()で別ウィンドウとして実装した方がよいかも、ということで、前回のソースコードに追記してみました。
出来上がりイメージ

Toplevel()を継承したOutlineウィンドウを追加しました。
ソースコード
ソースコードは以下の通りです。
import tkinter as tk
class Outline(tk.Toplevel):
def __init__(self, master=None, cnf={}, **kw):
super().__init__(master, cnf, **kw)
self.title("Outline")
self.canvas = tk.Canvas(self, background="lightblue")
self.canvas.pack(fill=tk.BOTH,expand=True)
master.update()
print(master.winfo_width(),master.winfo_height())
w, h = master.winfo_width()//5, master.winfo_height()//5
self.geometry(str(w)+"x"+str(h))
self.resizable(0,0)
self.attributes("-toolwindow",1)
self.attributes("-topmost",1)
def get_canvas(self):
return self.canvas
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.title("Canvas outline sample")
self.outline = None
self.outline_item = None
self.canvas = tk.Canvas(self, background="white")
self.canvas.pack(fill=tk.BOTH,expand=True)
self.canvas.bind("<1>",self.update_outline)
self.canvas.bind("<Configure>",self.update_outline)
self.focus_force()
self.outline_window = Outline(master=self)
def update_outline(self, event):
if self.outline == None or self.outline_item == None:
self.outline = o = tk.Canvas(self, background="lightblue")
c = self.canvas
width, height = c.winfo_width(), c.winfo_height()
w, h = width//5, height//5
x, y = width - w//2, height - h//2
self.outline_item = c.create_window(x, y, width=w, height=h, window=o)
return
if event.type == tk.EventType.Configure:
c = self.canvas
width, height = c.winfo_width(), c.winfo_height()
w, h = width//5, height//5
x, y = width - w//2, height - h//2
oi = self.outline_item
x0, y0 = c.coords(oi)
dx = x - x0
dy = y - y0
c.move(oi,dx,dy)
c.itemconfigure(oi,width=w,height=h)
self.outline_window.geometry(str(w)+"x"+str(h))
elif event.type == tk.EventType.Button:
c = self.canvas
x, y = event.x, event.y
c.create_rectangle(x, y, x+100, y+100)
o = self.outline
o.create_rectangle(x//5,y//5,(x+100)//5,(y+100)//5)
c = self.outline_window.get_canvas()
c.create_rectangle(x//5,y//5,(x+100)//5,(y+100)//5)
if __name__ == "__main__":
application = Application()
application.mainloop()
詳細
3~18行目まででOutlineウィンドウを追加しました。ポイントは9行目のmaster.update()でしょうか。これを実行することで待機中のイベントが実行されて、ウィンドウのサイズが計算されます。もし実行しない場合は、master.winfo_width()とmaster.winfo_height()が計算されていない状態、1と1になって、とっても小さいウィンドウが表示されます。ほとんど何も見えません。
13、14行目は、それぞれ、self.resizable(0,0)でサイズの変更をできないようにして、self.attributes("-toolwindow",1)で最小化と最大化のボタンを非表示にしています。15行目のself.attributes("-topmost",1)では、ずっと画面の一番上に表示されるように設定しています。常に前面で表示されるようになります。
57行目で、メインウィンドウの大きさが変わった時に同じようにアウトラインも大きさが変わるように動作させています。
まとめ
浮かんだウィンドウの方が、しっくりきますね。キャンバス上の実装はそのままでは移動できませんので、移動できるという点でも優れていると思います。本体部分がちゃんと作れれば、アウトライン部分も割と簡単に実装できそうですね。

