各位大大好!
先感謝各位在我上次發問迴圈問題時幫我解惑
目前程式可以在我打勾並開始執行後進行重複判斷圖片的功能
但是會造成視窗卡住,我有參考網路的作法加上平行任務的處理
不過執行時還是一樣會卡住完全不能操作視窗的按紐
有試過單獨按結束紐印出資料A=False,但是run2的迴圈好像沒有判定不成立而停止
請問是哪邊我沒有注意到呢? 再次感謝各位大大
import tkinter as tk # 導入TKinter
import pyautogui # 導入判斷GUI
import time # 導入時間
import cv2 # 導入照片處理
import numpy as np # 導入照片處理
import pyscreenshot as ImageGrab # 導入螢幕截圖
import threading # 導入多執行緒
from concurrent.futures import ThreadPoolExecutor# 導入平行任務
W = tk.Tk() # 建立主視窗 Frame
W.title('Monkey') # 設定視窗標題
window_width = W.winfo_screenwidth() # 取得電腦螢幕寬度
window_height = W.winfo_screenheight() # 取得電腦螢幕高度
width = 300 # 視窗大小
height = 300 # 視窗大小
left = int((window_width - width)/2) # 計算左上 x 座標
top = int((window_height - height)/2) # 計算左上 y 座標
W.geometry(f'{width}x{height}+{left}+{top}') # 視窗置中
W.iconbitmap('monkey.ico') # 檔案圖.ico
W.resizable(False, False ) # 設定 x 方向和 y 方向都不能縮放
var1=tk.BooleanVar() # 綁定第 1 個選項的類別變數
#var1.set(0) # 預設不選取
A=var1.get()
A=True
def run():
if var1.get() == 1:
run1()
def run1():
im = ImageGrab.grab(bbox=(2105,1054,2204,1148)) # 擷取指定範圍畫面(x1,y1,x2,y2)
im.save("b.png") # 儲存檔案
image1 = cv2.imread("a.png") # 圖片1
image2 = cv2.imread("b.png") # 圖片2
diff = cv2.subtract(image1,image2) # 判斷兩張圖
result = not np.any(diff)
if result is True:
print("Yes")
time.sleep(1)
return '1'
else:
cv2.imwrite("b.png",diff)
print("NO")
time.sleep(0.5)
return '0'
def run2():
global A
A=var1.get()
while var1.get():
run()
else:
print("無勾選")
def run3():
global A
if A==True:
var1.set(0)
A=False
else:
A=True
executor = ThreadPoolExecutor()
a1 = executor.submit(run2)
a2 = executor.submit(run3)
executor.shutdown()
tk.Checkbutton(W, text="執行",variable=var1,command=run).pack(pady=100) # 打勾選取功能
Start = tk.Button(W,text='開始', command=run2) # 按開始按鈕開始程式
Start.pack()
END2 = tk.Button(W,text='結束', command=run3) # 按結束按鈕結束程式
END2.pack()
W.mainloop() # 執行主程式
在你的程式內, tkinter
的 Checkbutton
按下、程式執行進入 while-loop
內不出來的話,程式不去跑 tkinter
程式碼, tkinter GUI 就不能動。
如果你要 tkinter GUI 能維持運作,且能控制 while-loop
,我第一時間也是想到 並行執行。
以下我用 Event Objects (用以應對執行緒的閒置)、你使用的 ThreadPoolExecutor (其實可單用 threading
模組,但你用了 ThreadPoolExecutor
我就加進去了)、搭配你的程式碼概要,做個「簡陋」的小範例給你參考:
(「簡陋」表示我省了很多安全性、維護性等語法,我自己看都有點怕,但寫太多又會變得很恐怖 + 我很懶……而且我不熟 tkinter,臨時只想的到這樣淺顯(?)的程式碼,後續就期望看有沒有人提出更便捷易讀的方法或程式碼吧~)
## Tested @[Python 3.10.9];
##
import tkinter as tk
import time, threading
from concurrent.futures import ThreadPoolExecutor
##
## myFlagsC: 全域變數用類別; 亦可用作執行緒間的通訊;
## Quit: 退出程式用旗標; Run1: Run1-main 的啟用旗標;
class myFlagsC: Quit, Run1 = False, False
##
def myRun1():
"""就只是 myRun1;"""
while not myFlags.Quit:
myEvent.wait() ## 停等 myEvent 狀態/旗標;
while myFlags.Run1:
print("myRun1...", end = ' ', flush = True)
time.sleep(1.25)
print("End;")
myEvent.clear() ## 重設 myEvent 狀態;
##
def myGUI1():
"""就只是 myGUI1;"""
def myRunCBClick():
"""myRunCB 的 Click 事件用;"""
myFlags.Run1 = var1.get() ## 規避(?) threading 與 tkinter 模組/物件之間的存取問題;
myEvent.set() ## 觸發/設定 myEvent 狀態/旗標; 用以啟動閒置的 myRun1-Thread;
##
def myQuit1():
"""結束程式用;"""
## 下 2 行用以結束 myRun1-Thread;
myFlags.Quit, myFlags.Run1 = True, False
if not myEvent.is_set(): myEvent.set()
## 這邊我沒設置對 myRun1-Thread 的狀態檢查就關閉 tkinter GUI,請自行設置;
## 不然會看到 tkinter GUI 已被關閉而 myRun1-Thread 還在跑一陣子的狀況,
## 導致用戶誤會程式運行狀態。
tRoot.destroy()
## Setup a tkinter;
tRoot = tk.Tk()
## 下 1 行: 當 tkinter 被以 GUI 視窗右上角 [X] 關閉時,執行 myQuit1();
tRoot.protocol("WM_DELETE_WINDOW", myQuit1)
## 不設定 "WM_DELETE_WINDOW" 會導致:
## 在 myRun1-Thread 還在執行時,就使用視窗右上角 [X] 或是 [Alt]+[F4] 而關閉 GUI 的話,
## myRun1-Thread 會因沒被設置結束狀態而維持執行;
##
(var1 := tk.BooleanVar()).set(False)
##
tRoot.title('aTkinter')
(myRunCB := tk.Checkbutton(
tRoot, text = "執行",
variable = var1,
command = myRunCBClick
)).pack()
(myQuitB := tk.Button(
tRoot, text = '結束',
command = myQuit1
)).pack()
tRoot.mainloop()
## init-setup
myFlags = myFlagsC()
## Setup a Event: myEvent: 用以啟動 myRun1() 的 while(myFlags.Run1)-loop ;
myEvent = threading.Event()
##
with ThreadPoolExecutor() as executor:
executor.submit(myRun1)
executor.submit(myGUI1)
##
您可以使用 Thread 或 multiprocessing 模組來處理多重任務,以避免 UI 被卡住的情況。
import cv2
import numpy as np
import time
from threading import Thread
class ImageCompare:
def __init__(self):
self.A = False
def compare(self, image1, image2):
# 比較圖像
# 返回True或False
pass
def run1(self):
# 讀取圖像
# 如果A=True,則重複判斷圖片
while True:
if self.A:
# 讀取圖片1和圖片2
image1 = cv2.imread("image1.jpg")
image2 = cv2.imread("image2.jpg")
result = self.compare(image1, image2)
if result:
# 如果圖像不同,則輸出結果
print("Images are different")
else:
# 如果圖像相同,則等待一段時間再次比較
time.sleep(1)
def run2(self):
while True:
# 每隔一秒讀取一次A的值
# 如果A=False,則退出迴圈
if not self.A:
break
time.sleep(1)
def start(self):
# 開始執行重複判斷圖片的功能
self.A = True
thread1 = Thread(target=self.run1)
thread2 = Thread(target=self.run2)
thread1.start()
thread2.start()
def stop(self):
# 停止重複判斷圖片的功能
self.A = False
if __name__ == "__main__":
image_compare = ImageCompare()
image_compare.start()
範例程式僅供參考