tkinter watch clipboard GetMessage нет возвращаемого значения - PullRequest
0 голосов
/ 13 февраля 2019

Я хочу подключить приложение Clipboard для win10.
Например: когда мы копируем текст 00-22-33-11-22 Mac Address из notepad.exe, окно tk получает текст и переводит mac address в имя машины.


, но у tkinter нет событий буфера обмена.
, поэтому я вызываю win32api
я ищу документ pywin32, нашел win32clipboard.SetClipboardViewer
, но создание окна просмотра буфера обмена очень сложно
я ищу MSDN, нашел, что AddClipboardFormatListener рекомендуется, этот метод проще, чем SetClipboardViewer. MSDN Создание прослушивателя формата буфера обмена
я использовал его, но GetMessage всегда блокировался

import tkinter as tk
import time
import threading as thrd
import win32gui
import win32clipboard
import win32api
import win32con
import ctypes
from ctypes.wintypes import MSG
from ctypes import byref


def selfevent(root):
    print("thrd start")
    hwnd = int(root.frame(), 16)
    done = ctypes.windll.user32.AddClipboardFormatListener(hwnd)
    print("done=", done)
    if done:
        wmsg = None
        print("begin GetMessage")
        wmsg = win32gui.GetMessage(None, 0, 0)
        # wmsg = MSG()
        # ctypes.windll.user32.GetMessageA(byref(wmsg), 0, 0, 0)
        print("GetMessage", wmsg.message(), win32api.GetLastError())
        if wmsg:
            print("msg=", wmsg)
            print(ctypes.windll.user32.RemoveClipboardFormatListener(hwnd))


if __name__ == "__main__":
    root = tk.Tk()
    root.title("tktest")
    root.geometry("600x400")
    # root.bind("<<foo>>", vectrl)
    print("begin")
    txt = tk.Entry(root)
    txt.pack()
    bt2 = tk.Button(root, text="GetClipboardSequenceNumber", command=lambda: print("sn=", win32clipboard.GetClipboardSequenceNumber()))
    bt2.pack()
    t = thrd.Thread(target=selfevent, args=(root,))
    t.setDaemon(True)
    t.start()
    root.mainloop()

как получить сообщение WM_CLIPBOARDUPDATE?


мой английскийочень бедныйрезультат выполнения:

begin
thrd start
done= 1
begin GetMessage

Я копирую что угодно, GetMessage всегда блокируется, нет возврата.
AddClipboardFormatListener успешно.GetMessage (hwnd или None, 0,0)
Результаты совпадают.

1 Ответ

0 голосов
/ 15 февраля 2019

Я изучил GetMessage, в основном потоке, использование AddClipboardFormatListener для регистрации, использование GetMessage - это нормально, но в новом потоке GetMessage всегда не имеет возвращаемого значения.
Я рассмотрелво многих сообщениях на форуме и в основном упоминается, что есть проблемы с многопоточностью tk.
@stovfl упоминал пост, который я прочитал.Я думаю, что after не очень хорошая идея.
after потребляет производительность основного потока и влияет на отображение пользовательского интерфейса.Используйте событие для общения на странице в vb.net.Поэтому я искал документацию по tk и обнаружил event_generate.
. Тест показал, что многопоточность не влияет на event_generate.
В сообщениях на форуме я исправил некоторые дефекты event_generateи учитывая мое решение.

Мой код демонстрирует мониторинг буфера обмена и запуск многопоточной задачи с помощью кнопки (обход всех файлов в каталоге пути, поиск общего количества файлов), отображение пользовательского интерфейса незатронуто блокирование задачи.

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, тест пройден.(Непрерывное нажатие на кнопку, открытие 12 рабочих потоков, проблем не обнаружено, поток пользовательского интерфейса работает гладко)
Если в коде произошла непредвиденная ошибка, проверьте tk * .dll в каталоге установки python.
Есть информация, чтоtk86t.dll поддерживает многопоточность, tk86.dll не поддерживается.
Спасибо @FabienAndre, @BryanOakley, @ stovfl и всем участникам обсуждения.Вы дали мне вдохновение для решения этой проблемы.
Если вы чувствуете, что у этого решения есть некоторые недостатки, пожалуйста, дайте мне знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...