いくつかのボタンをクリックしたときに時間がかかる操作を行うtkinter GUIを作成しました。私のケースには2つの大きな問題があります。私は、tkinterが一貫性のない方法でクラッシュする原因と考えると思います。新しいスレッドでTkinterのpythonがクラッシュしてメインスレッドにログオンしようとしました
1)新しいスレッドの出力を、メインスレッド上で実行されるスクロールされたテキストウィジェットに記録したいと思っています。これは良い考えではありません。今すぐ私はそれを行うTextHandlerオブジェクトをスレッドに送信する(私のコードを参照してください)
2)新しいスレッドからメインスレッドへのロギングを避けるために、私はスレッドが完了し、メインスレッドからテキストウィジェットにログを記録しますが、ボタンが押されたときに呼び出される関数でスレッドが作成されると、メインループはどのように認識されますか?
class TextHandler(logging.Handler):
"""This class allows you to log to a Tkinter Text or ScrolledText widget"""
def __init__(self, text):
# run the regular Handler __init__
logging.Handler.__init__(self)
# Store a reference to the Text it will log to
self.text = text
def emit(self, record):
msg = self.format(record)
def append():
self.text.configure(state='normal')
self.text.insert(tkinter.END, msg + '\n')
self.text.configure(state=DISABLED)
# Autoscroll to the bottom
self.text.yview(tkinter.END)
# This is necessary because we can't modify the Text from other threads
self.text.after(0, append)
class GUI(Frame):
def __init__(self, parent, logger, st):
....initialize stuff...
def _startComputations(self):
if (self._paths == ""):
self._logger.warn("No drive paths found, please add a path")
else:
if (self._flexinput == ""):
self._logger.warn(self._flexinput)
self._logger.warn("No file path,continuing with standard KM estimation")
self.myThread = StandardUsecases(self._paths, self._input,
self._logger, self._standard_choices,
self._numberOfoccurences_usecases,
self._all_logs, self._unique_countries,
self.bt, self.bt2, self.bt3,
self._flexinput)
self.myThread.start()
self._all_jsons = self.myThread._original_json_list
self._general_json = self.myThread._general_json_data_list
def _KM_Button(self):
self._all_logs[:] = []
self.bt = Button(self.frame6, text='1-Main Usecases', font = "Helvetica 11 bold italic",
command = self._startComputations,relief = RAISED, bd= 6, bg = "pale green", fg = 'black')
self.bt.pack(side = LEFT)
def initGUI(self):
self.parent.title("DC Statistics calculator")
self.pack(fill=BOTH, expand=True)
self._Drive_Paths()
self._Flexray_Path()
#self._UserInput()
self._KM_Button()
self._countButton()
self._exportButton()
self._helpButton()
def main():
root = Tk()
root.geometry("700x800+400+400")
st = scrolledtext.ScrolledText(root, state='disabled')
st.configure(font="Times 13")
st.pack(side=BOTTOM, fill='both',expand='yes')
text_handler = TextHandler(st)
logger = logging.getLogger()
logger.addHandler(text_handler)
app = GUI(root, logger, st)
root.mainloop()
if __name__ == '__main__':
main()
ボタンを押すと_startComputations関数が呼び出され、スレッドが作成されます。 _loggerオブジェクトを送信して、私が新しいスレッドにいる間にScrolledTextウィジェットにログすることができます。しかし、ほとんどの場合、「python.exeは動作を停止しました」または共有オブジェクトで呼び出されたtcl_appendlimitedtoobjのようなクラッシュを受けます。
新しいスレッドからログを記録したくない場合は、ボタンを押した後に呼び出される関数で新しいスレッドが作成されるため、メインループで新しいスレッドが完了したかどうかを知ることができますか?あなたが別のスレッドにTextHandler
のインスタンスと一緒にあなたのtext
ウィジェットを渡すので、あなたのエラーがoccures - 私がコメントで言ったように
はあなたに
あなたのコードはスレッドセーフではないと思います。これは、テキストウィジェットを 'TextHandler'のインスタンスと一緒に別のスレッドに渡すためです。 'TextHandler'のいくつかのメソッドは' text'ウィジェット(例えば 'emit')のメソッドをキャストし、メインスレッドからキャストする必要があります。あなたのコードは[MCVE](http://stackoverflow.com/help/mcve)ではないので、あなたが修正する必要があることを伝えるのは難しいですが、もっと簡単なオプションがあります。ロギング目的で 'stdout'、ウィジェットやファイルに' print'を直接ハイジャックすることもできます! – CommonSense
「ハイジャック」の部分について説明できますか?また、方法はありますか?メインスレッドは新しいスレッドが完了したことを知りますか?私はそれを見るので、tkinter mainloopは新しいスレッドについて「知っていません」ので、スレッド以外の場所でis_alive()メソッドを呼び出すことはできません – Dimitrios
別のアイデア:あなたは "コミット" ['Queue'](https://docs.python.org/3/library/asyncio-queue.html)(これはスレッドセーフではありません)オブジェクトに/から送出されます。 ['after'](http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget)にチェックすることもできます(スレッドの' is_alive() 'ステータスをチェックしてチェックすることもできます)。 after-method) "loop"(関数はこのメソッドのおかげで自身を再スケジュールできます)。 "ハイジャック"部分の一般的な[アイデア](http://stackoverflow.com/questions/3333334/stdout-to-tkinter-gui)([example](http://stackoverflow.com/a/43805139/6634373)) - 'stdout'ものをリダイレクトします。 – CommonSense