tkinter watch clipboard GetMessage no return value

久未见 提交于 2019-12-02 13:23:35

I have studied GetMessage, in the main thread, using AddClipboardFormatListener to register, using GetMessage is normal, but in the new thread, GetMessage always has no return value.
I have reviewed many of the forum posts, and basically mentioned that there are problems with multithreading of tk.
@stovfl mentioned the post, I read. I think that after is not a good idea.
after consumes main thread performance and affects UI display. Use event to communicate on the page in vb.net. So I searched the tk documentation and found event_generate.
The test found that event_generate does not seem to be affected by multithreading.
Through the forum posts, I have completed some of the defects of event_generate and given my solution.

My code demonstrates monitoring the clipboard and launching a multi-threaded task with the button (traversing all the files in the path directory, finding the total number of files), the UI display is not affected by the task blocking.

import tkinter as tk
import tkinter.ttk as ttk
import win32clipboard
import threading as thrd
import time
import os
from queue import Queue


def watchClip(top):
    lastid = None
    print("StartWatch")
    while True:
        time.sleep(0.01)
        nowid = win32clipboard.GetClipboardSequenceNumber()
        # print(nowid, lastid)
        if not lastid or (lastid != nowid):
            lastid = nowid
            top.event_generate("<<clipUpdateEvent>>", when="tail")


def workButton(top, path, outQueue):
    allcount = 0
    print("StartSearch")
    for root, dirs, files in os.walk(path):
        allcount += len(files)
        top.clipboard_clear()
        top.clipboard_append(allcount)
    outQueue.put_nowait(allcount)
    # top.event_generate("<<searchFin>>", data={"result": allcount}, when="tail")
    top.event_generate("<<searchFin>>", data=f"result={allcount}", when="tail")


def bind_event_data(widget, sequence, func, add=None):
    def _substitute(*args):
        def evt():
            return None  # simplest object with __dict__
        try:
            evt.data = eval(args[0])
        except Exception:
            evt.data = args[0]
        evt.widget = widget
        return (evt,)

    funcid = widget._register(func, _substitute, needcleanup=1)
    cmd = '{0}if {{"[{1} %d]" == "break"}} break\n'.format('+' if add else '', funcid)
    widget.tk.call('bind', widget._w, sequence, cmd)


if __name__ == "__main__":
    top = tk.Tk()
    top.title("tktest")
    top.geometry("300x200")
    rsltQueue = Queue()
    # top.bind("<<foo>>", vectrl)
    print("begin")
    lbl = tk.Label(top, text="clipboard", width=30, height=3)
    lbl.pack()
    lblrslt = tk.Label(top, text="SearchResult", width=40, height=3)
    lblrslt.pack()
    prb = ttk.Progressbar(top, length=100, mode="indeterminate")
    prb.pack()
    txt = tk.Entry(top, width=20)
    txt.pack()
    prb.start(interval=10)
    t = thrd.Thread(target=watchClip, args=(top,), daemon=True)
    t.start()

    def searchPath():
        t = thrd.Thread(target=workButton, args=(top, "c:", rsltQueue), daemon=True)
        t.start()
    bt2 = tk.Button(top, text="SearchPath", command=searchPath)
    bt2.pack()
    clipText = ""

    def dealCUE(event):
        global clipText
        try:
            clipText = top.clipboard_get()
        except tk.TclError:
            pass
        lbl["text"] = clipText

    def dealSF(event):
        # lblrslt["text"] = f"allFileCount={rsltQueue.get()}"
        # lblrslt["text"] = event.data["result"]
        lblrslt["text"] = event.data
    top.bind("<<clipUpdateEvent>>", dealCUE)
    # top.bind("<<searchFin>>", dealSF)
    bind_event_data(top, "<<searchFin>>", dealSF)
    top.mainloop()

Python 3.7.2, os win10 1151, the test passed. (Continuous click on the button, open 12 worker threads, no problems found, UI thread is smooth)
If the code has an unexpected error, check tk*.dll in the python installation directory.
There is information that tk86t.dll supports multithreading, tk86.dll is not supported.
Thanks to @FabienAndre, @BryanOakley ,@stovfl and everyone in the discussion. You gave me the inspiration to solve this problem.
If you feel that this solution has some flaws, please let me know.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!