1

Tkinter を使用して、外部スレッドを開始するボタンを作成しました。そして、外部スレッドの stdout および stderr にパイプされる tkinter Textbox 。外部スレッドの実行中に、このエラーが 1 回だけ発生しました。

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python33\lib\tkinter\__init__.py", line 1475, in __call__
    return self.func(*args)
  File "C:\Python33\lib\tkinter\__init__.py", line 2923, in set
    self.tk.call((self._w, 'set') + args)
_tkinter.TclError: bad option "set TCL VALUE NUMBER": must be activate, cget, configure, delta, fraction, get, identify, or set

再発はしていませんが、何が原因なのか知りたいです。エラーは外部スレッドからではなく、GUI を実行しているメイン スレッドからのもののようです。内部 Tkinter エラーでしょうか?

4

1 に答える 1

2

これはおそらく少し遅いですが、とにかく...

私自身の調査では、'when="tail"' を event_generate 呼び出しに追加するというアドバイスが見つかりました。これにより、イベントが正しくキューに入れられます。そうしないと、Tk イベント ルールがイベント キューに対しておかしな動作をする可能性があります。これは (まだ?) Tkinter で文書化されていません。

それでも、私自身の調査 (および経験!) によると、Tkinter は基本的にスレッドセーフではないことがわかりました。これは、独自のスレッドの外側で Tkinter を使用して何かを行うことを保証できないことを意味します。確かに、最も安全な方法はイベントを生成することですが、これでも定期的に例外がスローされます。これは、イベントを追加するために Tkinter 内部がスレッドセーフではないことを示しています。通常、これらの例外は存続可能であるため、再試行を使用して別のクラックを作成します。これはほとんど機能します。

retries = 3
done = False
unknownException = False
while (retries != 0) and (not done) and (not unknownException):
    try:
        self._applicationWindow.event_generate("<<MyNewEvent>>", when="tail")
        done = True
    except Tk.TclError as err:
        if "application has been destroyed" in err.message:
            # If application is destroyed, this event is not needed
            done = True
        elif ("expected boolean value but got" in err.message) or ("bad option" in err.message): 
            # These are bugs in Tk/Tcl/Tkinter, not in our code.  They seem to be uncommon,
            # and seem to be survivable.  Hopefully retrying will have the system in a
            # slightly different state where this doesn't happen next time.
            print "Tkinter/Tk/Tcl error, retry " + str(retries)
            retries = retries - 1
        else:
            unknownException = True
    except:
        unknownException = True

# Unlock afterwards
self._eventLock.release()

if not done:
    # If retries haven't helped or we have an exception which isn't one we know about,
    # then pass the exception up the tree.
    raise   

スレッドセーフでない UI レイヤーは 1 つのことです。これは設計を簡素化するものと思われるため、ほぼ許容できます。ただし、スレッドセーフでないイベント キューは、プログラミング入門クラスでは不合格にすべきです。Tkinter は壊れており、実際のアプリケーションに使用すべきではないと結論付けている場合は、クラブに参加してください。私に関する限り、Tkinter の問題に対する正しい修正は、目的に合った別の UI レイヤーを使用することです。:/

PS。Python 2.7.6 を使用しています。Python 3 の YMMV。

于 2014-04-28T12:49:54.680 に答える