如何實(shí)現(xiàn)Python編寫的圖形界面可以自由拖動(dòng)
一、問題的提出
我們使用python中的tkinter進(jìn)行編程時(shí),往往需要一種功能就是我們可以隨意拖動(dòng)這個(gè)界面,放置在任何位置,而不是只能拖動(dòng)標(biāo)題欄,這樣使我們的程序用起來更加便捷和絲滑。
二、問題分析
如果要實(shí)現(xiàn)這種方法,我們可以定義函數(shù),也可以做一個(gè)裝飾器,到時(shí)相當(dāng)于給我們的程序添加了一個(gè)新的功能一樣,這樣的邏輯就更好理解了。
三、問題的解決
1. 直接用函數(shù)的方法
這種方法是最原始的,我們可以直接把它加入到以函數(shù)寫法的程序中,通過定義兩個(gè)函數(shù),包括on_drag_start和on_drag_motion,通過獲取鼠標(biāo)的新位置,來實(shí)現(xiàn)界面的位置移動(dòng)。
import tkinter as tk def on_drag_start(event): global x_start, y_start x_start = event.x_root y_start = event.y_root def on_drag_motion(event): global x_start, y_start delta_x = event.x_root - x_start delta_y = event.y_root - y_start x_new = root.winfo_x() + delta_x y_new = root.winfo_y() + delta_y root.geometry(f"+{x_new}+{y_new}") x_start = event.x_root y_start = event.y_root root = tk.Tk() root.geometry("400x300") # 僅將事件處理程序綁定到根窗口的背景上 root.bind('<Button-1>', on_drag_start) root.bind('<B1-Motion>', on_drag_motion) # 添加一個(gè)示例控件 button = tk.Button(root, text="按鈕") button.pack(pady=20) label = tk.Label(root, text="移動(dòng)窗口") label.pack(pady=20) root.mainloop()
2. 通過裝飾器來實(shí)現(xiàn)
雖然函數(shù)的方法就可以實(shí)現(xiàn),那么我們可以把這兩個(gè)函數(shù)放在裝飾器里面,調(diào)用時(shí)用@函數(shù)名就可以了。在下面的代碼中,我們定義了一個(gè)draggable_window這個(gè)裝飾器,可以用于為函數(shù)添加新的功能。我們create_window()前面添加了@draggable_window,這樣就可以為新窗口添加新功能了。
import tkinter as tk def draggable_window(func): def wrapper(*args, **kwargs): root = func(*args, **kwargs) def on_drag_start(event): root.x_start = event.x_root root.y_start = event.y_root def on_drag_motion(event): delta_x = event.x_root - root.x_start delta_y = event.y_root - root.y_start x_new = root.winfo_x() + delta_x y_new = root.winfo_y() + delta_y root.geometry(f"+{x_new}+{y_new}") root.x_start = event.x_root root.y_start = event.y_root root.bind('<Button-1>', on_drag_start) root.bind('<B1-Motion>', on_drag_motion) return root return wrapper @draggable_window def create_window(): root = tk.Tk() root.geometry("400x300") # 添加控件 button = tk.Button(root, text="按鈕") button.pack(pady=20) label = tk.Label(root, text="移動(dòng)窗口") label.pack(pady=20) return root # 啟動(dòng)窗口 root = create_window() root.mainloop()
DraggableWindow 類封裝了窗口的創(chuàng)建、拖動(dòng)功能和控件的添加。
on_drag_start 和 on_drag_motion 方法分別處理拖動(dòng)的起點(diǎn)和拖動(dòng)過程。
add_widgets 方法用于向窗口添加按鈕和標(biāo)簽。
run 方法啟動(dòng) mainloop(),以顯示窗口。
3. 把裝飾器寫成類去裝飾另一個(gè)程序添加新功能
我們還可以定義完裝飾器后放入一個(gè)類中,直接去修飾另一個(gè)類,代碼如下,注意裝飾器的位置。這樣的寫法邏輯分明,條理清楚,不想使用這個(gè)拖動(dòng)這個(gè)功能,直接刪除裝飾器的類,就可以了。
import tkinter as tk # 定義拖動(dòng)功能的裝飾器 def draggable(func): def wrapper(self, *args, **kwargs): func(self, *args, **kwargs) def on_drag_start(event): self.x_start = event.x_root self.y_start = event.y_root def on_drag_motion(event): delta_x = event.x_root - self.x_start delta_y = event.y_root - self.y_start x_new = self.root.winfo_x() + delta_x y_new = self.root.winfo_y() + delta_y self.root.geometry(f"+{x_new}+{y_new}") self.x_start = event.x_root self.y_start = event.y_root # 將拖動(dòng)事件綁定到窗口 self.root.bind('<Button-1>', on_drag_start) self.root.bind('<B1-Motion>', on_drag_motion) return wrapper class DraggableWindow: @draggable def __init__(self): self.root = tk.Tk() self.root.geometry("400x300") # 添加控件 self.add_widgets() def add_widgets(self): button = tk.Button(self.root, text="按鈕") button.pack(pady=20) label = tk.Label(self.root, text="移動(dòng)窗口") label.pack(pady=20) def run(self): self.root.mainloop() # 啟動(dòng)窗口 app = DraggableWindow() app.run()
上面代碼中,draggable 裝飾器應(yīng)用到類的 __init__ 方法上,這樣在類實(shí)例化時(shí),窗口會(huì)自動(dòng)綁定拖動(dòng)事件。
裝飾器內(nèi)部通過 self.root.bind 綁定拖動(dòng)功能。
這樣封裝后,拖動(dòng)功能的邏輯被封裝在裝飾器中,類的代碼保持清晰簡(jiǎn)潔。
三、學(xué)后總結(jié)
1. 未來在編程中,常用的功能貌似都可以包裝成一個(gè)裝飾器或者一個(gè)可以調(diào)用的模塊的形式,這樣實(shí)現(xiàn)主程序和一些功能性的組件分離,修改、調(diào)試程序就更加方便。
2. 今天的學(xué)習(xí)中,從單個(gè)的函數(shù)實(shí)現(xiàn),到簡(jiǎn)單的裝飾器以及類裝飾器的實(shí)現(xiàn),復(fù)雜程度進(jìn)一步提升,應(yīng)用的邏輯也更多加清晰。
3. 學(xué)習(xí)Python是一個(gè)認(rèn)識(shí)不斷加深的過程,像裝飾器這樣難理解的概念,如果單純從文字上理解比較困難,可以放在小項(xiàng)目中,逐步消化,增進(jìn)理解。
到此這篇關(guān)于如何實(shí)現(xiàn)Python編寫的圖形界面可以自由拖動(dòng)的文章就介紹到這了,更多相關(guān)Python圖形界面自由拖動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python3從零開始搭建一個(gè)語音對(duì)話機(jī)器人的實(shí)現(xiàn)
這篇文章主要介紹了Python3從零開始搭建一個(gè)語音對(duì)話機(jī)器人的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Python登錄QQ郵箱發(fā)送郵件的實(shí)現(xiàn)示例
本文主要介紹了Python登錄QQ郵箱發(fā)送郵件的實(shí)現(xiàn)示例,主要就是三步,登錄郵件、寫郵件內(nèi)容、發(fā)送,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>2023-08-08Python+Socket實(shí)現(xiàn)基于TCP協(xié)議的客戶與服務(wù)端中文自動(dòng)回復(fù)聊天功能示例
這篇文章主要介紹了Python+Socket實(shí)現(xiàn)基于TCP協(xié)議的客戶與服務(wù)端中文自動(dòng)回復(fù)聊天功能,結(jié)合實(shí)例形式分析了Python+Socket實(shí)現(xiàn)帶自動(dòng)回復(fù)功能的TCP聊天程序相關(guān)操作方法與注意事項(xiàng),需要的朋友可以參考下2017-08-08淺談python圖片處理Image和skimage的區(qū)別
這篇文章主要介紹了淺談python圖片處理Image和skimage的區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Python實(shí)現(xiàn)的NN神經(jīng)網(wǎng)絡(luò)算法完整示例
這篇文章主要介紹了Python實(shí)現(xiàn)的NN神經(jīng)網(wǎng)絡(luò)算法,結(jié)合完整實(shí)例形式分析了Python使用numpy、matplotlib及sklearn模塊實(shí)現(xiàn)NN神經(jīng)網(wǎng)絡(luò)相關(guān)算法實(shí)現(xiàn)技巧與操作注意事項(xiàng),需要的朋友可以參考下2018-06-06Python中基礎(chǔ)數(shù)據(jù)類型 set集合知識(shí)點(diǎn)總結(jié)
在本篇文章里小編給大家總結(jié)了一篇關(guān)于Python中基礎(chǔ)數(shù)據(jù)類型 set集合知識(shí)點(diǎn)總結(jié)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。2021-08-08