2010-12-29 28 views
1

私はtkinterで個人的に使うための非常に小さなプログラムを作っています。私は本当に奇妙な壁にぶつかりました。私はpywin32のバインディングとtkinterを混在させています。なぜなら、pywin32の構文と命名規則をすべて嫌うからです。そして、tkinterははるかに少ないコードでより多くの作業を行うように感じます。 pywin32のクリップボードの監視とtkinterのプログラムへの私のプログラムの反応との間の移行で、奇妙なことが起こっています。PyWin32を少し混ぜると、このTkinterコードがクラッシュするのはなぜですか?

ウィンドウとそのすべてのコントロールがtkinterで処理されています。 pywin32バインディングは、クリップボードが変わったときにクリップボードの監視とクリップボードへのアクセスを行っています。クリップボードがpywin32の作品を見ている方法について私が集めたことから、あなたはpywin32にあなたのウィンドウのhwnd値を提供する限り、あなたが望むものであればそれを動作させることができます。私はその部分をやっていて、プログラムが最初に始まるときに動作します。クリップボードが変更されても機能しないようです。

プログラムが起動すると、クリップボードをつかみ、それを検索ボックスと編集ボックスに入れます。クリップボードが変更されたとき、私が発射したいイベントは発射されています...プログラムが起動する前に完全に機能していたイベントが、今行われていることをするのではなく奇妙なハングを引き起こしています。クリップボードの内容をstdoutに出力することはできますが、クリップボードが変更されても同じデータをtkinterウィジェットに入れないでください。それは、クリップボードの変更通知によって解消された後に、私のtkinterウィジェットのいずれかとやりとりするようになると、それだけのようにハングアップします。

pywin32のエチケットは、私がtkinterを使っているプログラムに使用していたクリップボードを見ているサンプルコードを修正するのに欠けているようです。 Tkinterは明らかにスタックトレースやエラーメッセージを生成したくないので、pdbを使ってデバッグしようとしたらどうしたらいいか分からない。

は、ここでは、コードです:それは助けることができれば

#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() 

答えて

2

わからないが、私はそれはあなたが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) 
+1

私はそれはおそらく働くだろうと思うが、私は更新をトリガするために、メインループのいくつかの並べ替えにポーリングする必要がありますブール変数は他の場所に置かれます。 この質問にお答えいただきありがとうございます。私はちょっと待ってから、プロジェクト全体をwxPythonに変換しました。私はwxPythonがそれほど好きではありませんが、この小さなプロジェクトでは、それを完全に採用するだけの頭痛にはならないでしょう。 – Erlog

関連する問題