Python 为什么这个有效的Tkinter代码在与PyWin32混合时会崩溃?
所以我在tkinter做了一个非常小的程序供个人使用,我遇到了一堵非常奇怪的墙。我把tkinter和pywin32绑定混合在一起,因为我真的很讨厌pywin32的语法和命名约定,而且感觉tkinter可以用更少的代码完成更多的工作。奇怪的是在pywin32剪贴板监视和我的程序在tkinter中对它的反应之间的转换过程中发生的 我的窗口及其所有控件都在tkinter中处理。当剪贴板更改时,pywin32绑定正在执行剪贴板监视和剪贴板访问。从我收集的有关剪贴板监视pywin32片段的工作方式的信息来看,只要为pywin32提供窗口的hwnd值,您就可以使它与任何您想要的东西一起工作。我正在做那部分,当程序第一次启动时它就开始工作了。当剪贴板更改时,它似乎不起作用 当程序启动时,它抓取剪贴板并将其放入搜索框和编辑框中。当剪贴板被修改时,我想触发的事件正在触发…除了在程序启动之前完全有效的事件现在导致了奇怪的挂起,而不是它应该做的事情。如果剪贴板发生更改,我可以将剪贴板内容打印到标准,但不能将相同的数据放入tkinter小部件中。只有当它在被剪贴板更改通知触发后开始与我的tkinter小部件交互时,它才会像这样挂起 在将我正在使用的示例代码调整到tkinter使用程序中时,我感觉有一些pywin32礼仪我错过了。Tkinter显然不喜欢生成堆栈跟踪或错误消息,而且我甚至都不知道如何使用pdb调试它 代码如下:Python 为什么这个有效的Tkinter代码在与PyWin32混合时会崩溃?,python,crash,tkinter,clipboard,pywin32,Python,Crash,Tkinter,Clipboard,Pywin32,所以我在tkinter做了一个非常小的程序供个人使用,我遇到了一堵非常奇怪的墙。我把tkinter和pywin32绑定混合在一起,因为我真的很讨厌pywin32的语法和命名约定,而且感觉tkinter可以用更少的代码完成更多的工作。奇怪的是在pywin32剪贴板监视和我的程序在tkinter中对它的反应之间的转换过程中发生的 我的窗口及其所有控件都在tkinter中处理。当剪贴板更改时,pywin32绑定正在执行剪贴板监视和剪贴板访问。从我收集的有关剪贴板监视pywin32片段的工作方式的信息来
#coding: utf-8
#Clipboard watching cribbed from ## {{{ http://code.activestate.com/recipes/355593/ (r1)
import pdb
from Tkinter import *
import win32clipboard
import win32api
import win32gui
import win32con
import win32clipboard
def force_unicode(object, encoding="utf-8"):
if isinstance(object, basestring) and not isinstance(object, unicode):
object = unicode(object, encoding)
return object
class Application(Frame):
def __init__(self, master=None):
self.master = master
Frame.__init__(self, master)
self.pack()
self.createWidgets()
self.hwnd = self.winfo_id()
self.nextWnd = None
self.first = True
self.oldWndProc = win32gui.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, self.MyWndProc)
try:
self.nextWnd = win32clipboard.SetClipboardViewer(self.hwnd)
except win32api.error:
if win32api.GetLastError () == 0:
# information that there is no other window in chain
pass
else:
raise
self.update_search_box()
self.word_search()
def word_search(self):
#pdb.set_trace()
term = self.searchbox.get()
self.resultsbox.insert(END, term)
def update_search_box(self):
clipboardtext = ""
if win32clipboard.IsClipboardFormatAvailable(win32clipboard.CF_TEXT):
win32clipboard.OpenClipboard()
clipboardtext = win32clipboard.GetClipboardData()
win32clipboard.CloseClipboard()
if clipboardtext != "":
self.searchbox.delete(0,END)
clipboardtext = force_unicode(clipboardtext)
self.searchbox.insert(0, clipboardtext)
def createWidgets(self):
self.button = Button(self)
self.button["text"] = "Search"
self.button["command"] = self.word_search
self.searchbox = Entry(self)
self.resultsbox = Text(self)
#Pack everything down here for "easy" layout changes later
self.searchbox.pack()
self.button.pack()
self.resultsbox.pack()
def MyWndProc (self, hWnd, msg, wParam, lParam):
if msg == win32con.WM_CHANGECBCHAIN:
self.OnChangeCBChain(msg, wParam, lParam)
elif msg == win32con.WM_DRAWCLIPBOARD:
self.OnDrawClipboard(msg, wParam, lParam)
# Restore the old WndProc. Notice the use of win32api
# instead of win32gui here. This is to avoid an error due to
# not passing a callable object.
if msg == win32con.WM_DESTROY:
if self.nextWnd:
win32clipboard.ChangeClipboardChain (self.hwnd, self.nextWnd)
else:
win32clipboard.ChangeClipboardChain (self.hwnd, 0)
win32api.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, self.oldWndProc)
# Pass all messages (in this case, yours may be different) on
# to the original WndProc
return win32gui.CallWindowProc(self.oldWndProc, hWnd, msg, wParam, lParam)
def OnChangeCBChain (self, msg, wParam, lParam):
if self.nextWnd == wParam:
# repair the chain
self.nextWnd = lParam
if self.nextWnd:
# pass the message to the next window in chain
win32api.SendMessage (self.nextWnd, msg, wParam, lParam)
def OnDrawClipboard (self, msg, wParam, lParam):
if self.first:
self.first = False
else:
#print "changed"
self.word_search()
#self.word_search()
if self.nextWnd:
# pass the message to the next window in chain
win32api.SendMessage(self.nextWnd, msg, wParam, lParam)
if __name__ == "__main__":
root = Tk()
app = Application(master=root)
app.mainloop()
root.destroy()
不确定它是否有帮助,但我假设它在您从win32事件处理程序中调用更新时发生故障,tkinter可能不喜欢这样 通常的解决方法是通过after_idle()回调延迟更新 因此,尝试替换:
def OnDrawClipboard (self, msg, wParam, lParam):
if self.first:
self.first = False
else:
#print "changed"
self.word_search()
#self.word_search()
if self.nextWnd:
# pass the message to the next window in chain
win32api.SendMessage(self.nextWnd, msg, wParam, lParam)
为此:
def OnDrawClipboard (self, msg, wParam, lParam):
if self.first:
self.first = False
else:
#print "changed"
self.after_idle(self.word_search)
#self.word_search()
if self.nextWnd:
# pass the message to the next window in chain
win32api.SendMessage(self.nextWnd, msg, wParam, lParam)
我认为这可能会起作用,但我必须在某种主循环中进行轮询,以使用放置在别处的布尔变量触发更新。我感谢你试图回答这个问题。在这么长时间没有回应后,我只是将整个项目转换为wxPython。我真的不太喜欢wxPython,但是对于这个小项目来说,完全采用它就不那么令人头痛了。