次の例では、デフォルトで開く[ログ]タブ以外の他のタブに書き込むことができません。
#!/usr/bin/python
import os, sys
import threading
import time
from itertools import count
import tkinter
from tkinter import *
from tkinter import tix
import tkinter.tix
from tkinter.constants import *
import traceback, tkinter.messagebox
from tkinter import ttk
TCL_DONT_WAIT = 1<<1
TCL_WINDOW_EVENTS = 1<<2
TCL_FILE_EVENTS = 1<<3
TCL_TIMER_EVENTS = 1<<4
TCL_IDLE_EVENTS = 1<<5
TCL_ALL_EVENTS = 0
class GUI(threading.Thread):
def __init__(self):
""" thread init
defines some vars and starts stuff when the class is called (gui=GUI())
"""
self.root=tkinter.tix.Tk()
z = self.root.winfo_toplevel()
z.wm_title('minimal example')
if z.winfo_screenwidth() <= 800:
z.geometry('790x590+10+10')
else:
z.geometry('890x640+10+10')
frame1 = self.MkMainNotebook()
frame2 = self.MkMainStatus()
frame1.pack(side=TOP, expand=1, fill=BOTH, padx=4, pady=4)
frame2.pack(side=BOTTOM, fill=X)
z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.stop())
threading.Thread.__init__(self)
def run(self):
""" thread start
kick starts the main loop when the thread start()
"""
self.root.mainloop()
def stop(self):
""" escape plan
Exits gui thread
"""
self._stop()
raise SystemExit
def MkMainStatus(self):
""" status bar
"""
top = self.root
w = tkinter.tix.Frame(top, relief=tkinter.tix.RAISED, bd=1)
self.exitbutton = tkinter.Button(w, text='Exit GUI Thread', width=20, command=lambda self=self: self.stop())
self.exitbutton.grid(row=0, column=0, sticky=E, padx=3, pady=3)
return w
def MkMainNotebook(self):
""" the tabs frame
defines the tabs
"""
top = self.root
w = tkinter.tix.NoteBook(top, ipadx=5, ipady=5, options="""
tagPadX 6
tagPadY 4
borderWidth 2
""")
top['bg'] = w['bg']
w.add('log', label='Log', underline=0,
createcmd=lambda w=w, name='log': self.MkLog(w, name))
w.add('pro', label='Progress', underline=0,
createcmd=lambda w=w, name='pro': self.MkProgress(w, name))
w.add('set', label='Settings', underline=0,
createcmd=lambda w=w, name='set': self.MkSettings(w, name))
return w
def MkSettings(self, nb, name):
""" TODO: settings tab
"""
w = nb.page(name)
options="label.width %d label.anchor %s entry.width %d" % (10, tkinter.tix.E, 13)
settings_scr_win = tix.ScrolledWindow(w, width=400, height=400)
settings_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.SettingsFrame = settings_scr_win.window
def MkProgress(self, nb, name):
""" TODO: progress tab
"""
w = nb.page(name)
options = "label.padX 4"
progress_scr_win = tix.ScrolledWindow(w, width=400, height=400)
progress_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.ProgressFrame = progress_scr_win.window
def MkLog(self, nb, name):
""" log
"""
w = nb.page(name)
options = "label.padX 4"
log_scr_win = tix.ScrolledWindow(w, width=400, height=400)
log_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.LogFrame = log_scr_win.window
def main(argv):
""" main function
Keyword arguments:
args[0] --
args[1] --
Returns:
None
"""
#GUI
gui=GUI()
gui.start()
time.sleep(1) # give it a sec to draw the gui...
tix.Label(gui.LogFrame, text=("log")).pack()
tix.Label(gui.SettingsFrame, text=("settings")).pack()
tix.Label(gui.ProgressFrame, text=("progress")).pack()
return None
if __name__ == "__main__":
sys.exit(main(sys.argv))
コンソールトレースバック:
Traceback (most recent call last):
File "C:\minimal example.py", line 132, in <module>
sys.exit(main(sys.argv))
File "C:\minimal example.py", line 126, in main
tix.Label(gui.SettingsFrame, text=("settings")).pack()
AttributeError: 'GUI' object has no attribute 'SettingsFrame'
これは、gui.SettingsFrameまたはgui.ProgressFrameでウィジェットを作成しようとするたびに発生します。main()でtime.sleep(1)を増やし、tix.Label部分が開始する前にタブをクリックすると、tabsコマンドが呼び出されてからコードが機能します。
では、事前にgui.SettingsFrameとgui.ProgressFrameを宣言するにはどうすればよいですか?main()のtix.Labelに到達する前に、コードのタブを通過できますか?
注:スレッド化する必要があります。これは、12の異なることを実行し、マルチスレッド化され、いくつかのプロセスを持つ、より大きなプログラムの一部です。
ありがとう
編集1: フレームを参照する代わりに、クラスにメソッドを追加できます。
def print_log(self, text):
""" log printer
"""
tix.Label(self.LogFrame, text=(text)).pack()
def print_progress(self, text):
""" log printer
"""
tix.Label(self.ProgressFrame, text=(text)).pack()
def print_settings(self, text):
""" log printer
"""
tix.Label(self.SettingsFrame, text=(text)).pack()
そしてそれらをメインで呼び出します:
#tix.Label(gui.LogFrame, text=("log")).pack()
gui.print_log("log")
#tix.Label(gui.SettingsFrame, text=("settings")).pack()
gui.print_settings("settings")
#tix.Label(gui.ProgressFrame, text=("progress")).pack()
gui.print_progress("progress")
しかし、結果は同じです:
Traceback (most recent call last):
File "C:\minimal example.py", line 150, in <module>
sys.exit(main(sys.argv))
File "C:\minimal example.py", line 143, in main
gui.print_settings("settings")
File "C:\minimal example.py", line 124, in print_settings
tix.Label(self.SettingsFrame, text=(text)).pack()
AttributeError: 'GUI' object has no attribute 'SettingsFrame'
編集2 :ブライアンオークリーによる非常に素早い迅速で簡単な修正:
#!/usr/bin/python
import os, sys
import threading
import time
from itertools import count
import tkinter
from tkinter import *
from tkinter import tix
import tkinter.tix
from tkinter.constants import *
import traceback, tkinter.messagebox
from tkinter import ttk
TCL_DONT_WAIT = 1<<1
TCL_WINDOW_EVENTS = 1<<2
TCL_FILE_EVENTS = 1<<3
TCL_TIMER_EVENTS = 1<<4
TCL_IDLE_EVENTS = 1<<5
TCL_ALL_EVENTS = 0
class GUI(threading.Thread):
def __init__(self):
""" thread init
defines some vars and starts stuff when the class is called (gui=GUI())
"""
self.root=tkinter.tix.Tk()
z = self.root.winfo_toplevel()
z.wm_title('minimal example')
if z.winfo_screenwidth() <= 800:
z.geometry('790x590+10+10')
else:
z.geometry('890x640+10+10')
frame1 = self.MkMainNotebook()
frame2 = self.MkMainStatus()
frame1.pack(side=TOP, expand=1, fill=BOTH, padx=4, pady=4)
frame2.pack(side=BOTTOM, fill=X)
z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.stop())
threading.Thread.__init__(self)
def run(self):
""" thread start
kick starts the main loop when the thread start()
"""
self.root.mainloop()
def stop(self):
""" escape plan
Exits gui thread
"""
self._stop()
raise SystemExit
def MkMainStatus(self):
""" status bar
"""
top = self.root
w = tkinter.tix.Frame(top, relief=tkinter.tix.RAISED, bd=1)
self.exitbutton = tkinter.Button(w, text='Exit GUI Thread', width=20, command=lambda self=self: self.stop())
self.exitbutton.grid(row=0, column=0, sticky=E, padx=3, pady=3)
return w
def MkMainNotebook(self):
""" the tabs frame
defines the tabs
"""
top = self.root
w = tkinter.tix.NoteBook(top, ipadx=5, ipady=5, options="""
tagPadX 6
tagPadY 4
borderWidth 2
""")
top['bg'] = w['bg']
w.add('log', label='Log', underline=0)
#createcmd=lambda w=w, name='log': self.MkLog(w, name))
w.add('pro', label='Progress', underline=0)
#createcmd=lambda w=w, name='pro': self.MkProgress(w, name))
w.add('set', label='Settings', underline=0)
#createcmd=lambda w=w, name='set': self.MkSettings(w, name))
#log_it = w.subwidget('log')
#pro_it = w.subwidget('pro')
#set_it = w.subwidget('set')
self.MkLog(w, 'log')
self.MkProgress(w, 'pro')
self.MkSettings(w, 'set')
return w
def MkSettings(self, nb, name):
""" TODO: settings tab
"""
w = nb.page(name)
options="label.width %d label.anchor %s entry.width %d" % (10, tkinter.tix.E, 13)
settings_scr_win = tix.ScrolledWindow(w, width=400, height=400)
settings_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.SettingsFrame = settings_scr_win.window
def MkProgress(self, nb, name):
""" TODO: progress tab
"""
w = nb.page(name)
options = "label.padX 4"
progress_scr_win = tix.ScrolledWindow(w, width=400, height=400)
progress_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.ProgressFrame = progress_scr_win.window
def MkLog(self, nb, name):
""" log
"""
w = nb.page(name)
options = "label.padX 4"
log_scr_win = tix.ScrolledWindow(w, width=400, height=400)
log_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.LogFrame = log_scr_win.window
def print_log(self, text):
""" log printer
"""
tix.Label(self.LogFrame, text=(text)).pack()
def print_progress(self, text):
""" log printer
"""
tix.Label(self.ProgressFrame, text=(text)).pack()
def print_settings(self, text):
""" log printer
"""
tix.Label(self.SettingsFrame, text=(text)).pack()
def main(argv):
""" main function
Keyword arguments:
args[0] --
args[1] --
Returns:
None
"""
#GUI
gui=GUI()
gui.start()
time.sleep(1) # give it a sec to draw the gui...
tix.Label(gui.LogFrame, text=("log")).pack()
#gui.print_log("log")
tix.Label(gui.SettingsFrame, text=("settings")).pack()
#gui.print_settings("settings")
tix.Label(gui.ProgressFrame, text=("progress")).pack()
#gui.print_progress("progress")
return None
if __name__ == "__main__":
sys.exit(main(sys.argv))
ありがとう !
編集3:以下はスレッドセーフな実装です。ボーナスタイマーを追加しました:
#!/usr/bin/python
import os, sys
import threading
import queue
from queue import Empty
import time
from itertools import count
import tkinter
from tkinter import *
from tkinter import tix
import tkinter.tix
from tkinter.constants import *
import traceback, tkinter.messagebox
from tkinter import ttk
TCL_DONT_WAIT = 1<<1
TCL_WINDOW_EVENTS = 1<<2
TCL_FILE_EVENTS = 1<<3
TCL_TIMER_EVENTS = 1<<4
TCL_IDLE_EVENTS = 1<<5
TCL_ALL_EVENTS = 0
class GUI(threading.Thread):
def __init__(self):
""" thread init
defines some vars and starts stuff when the class is called (gui=GUI())
"""
self.root=tkinter.tix.Tk()
z = self.root.winfo_toplevel()
z.wm_title('minimal example')
if z.winfo_screenwidth() <= 800:
z.geometry('790x590+10+10')
else:
z.geometry('890x640+10+10')
frame1 = self.MkMainNotebook()
frame2 = self.MkMainStatus()
frame1.pack(side=TOP, expand=1, fill=BOTH, padx=4, pady=4)
frame2.pack(side=BOTTOM, fill=X)
z.wm_protocol("WM_DELETE_WINDOW", lambda self=self: self.stop())
threading.Thread.__init__(self)
def run(self):
""" thread start
kick starts the main loop when the thread start()
"""
self.root.mainloop()
def stop(self):
""" escape plan
Exits gui thread
"""
self._stop()
raise SystemExit
def MkMainStatus(self):
""" status bar
"""
top = self.root
w = tkinter.tix.Frame(top, relief=tkinter.tix.RAISED, bd=1)
self.status = tkinter.tix.Label(w, anchor=E, bd=1)
self.exitbutton = tkinter.Button(w, text='Exit GUI Thread', width=20, command=lambda self=self: self.stop())
self.print_queue=queue.Queue()
self.print_label()
self.status.grid(row=0, column=0, sticky=W, padx=3, pady=3)
self.exitbutton.grid(row=0, column=1, sticky=E, padx=3, pady=3)
return w
def print_label(self):
""" listner
listner
"""
rate=0.5 # seconds to re-read queue; 0.5=half a second, 1=a full second
counter = count(0, rate)
def update_func():
secs= str(counter.__next__())
try:
self.status.config(text=str("%s(secs): Processing queue..." % (secs.split('.'))[0]), fg=str("red"))
a = tix.Label(self.LogFrame, text=(self.print_queue.get(False)))
except Empty:
self.status.config(text=str("%s(secs): Waiting for queue..." % (secs.split('.'))[0]), fg=str("black"))
self.status.after(int(rate*1000), update_func)
else:
a.pack()
a.after(int(rate*1000), update_func)
update_func()
def MkMainNotebook(self):
""" the tabs frame
defines the tabs
"""
top = self.root
w = tkinter.tix.NoteBook(top, ipadx=5, ipady=5, options="""
tagPadX 6
tagPadY 4
borderWidth 2
""")
top['bg'] = w['bg']
w.add('log', label='Log', underline=0)
self.MkLog(w, 'log')
w.add('pro', label='Progress', underline=0)
self.MkProgress(w, 'pro')
w.add('set', label='Settings', underline=0)
self.MkSettings(w, 'set')
return w
def MkSettings(self, nb, name):
""" TODO: settings tab
"""
w = nb.page(name)
options="label.width %d label.anchor %s entry.width %d" % (10, tkinter.tix.E, 13)
settings_scr_win = tix.ScrolledWindow(w, width=400, height=400)
settings_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.SettingsFrame = settings_scr_win.window
def MkProgress(self, nb, name):
""" TODO: progress tab
"""
w = nb.page(name)
options = "label.padX 4"
progress_scr_win = tix.ScrolledWindow(w, width=400, height=400)
progress_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.ProgressFrame = progress_scr_win.window
def MkLog(self, nb, name):
""" log
"""
w = nb.page(name)
options = "label.padX 4"
log_scr_win = tix.ScrolledWindow(w, width=400, height=400)
log_scr_win.pack(side=tkinter.tix.TOP, padx=2, pady=2, fill='both', expand=1)
self.LogFrame = log_scr_win.window
def main(argv):
""" main function
Keyword arguments:
args[0] --
args[1] --
Returns:
None
"""
#GUI
gui=GUI()
gui.start()
gui.print_queue.put("log")
time.sleep(10) # timed release test
gui.print_queue.put("timed release test")
return None
if __name__ == "__main__":
sys.exit(main(sys.argv))
これはおそらく、スレッド化されたtkinterguiをコンソールアプリケーションに実装するための最も簡単な方法です。gui.print_queue.put()はprint()を置き換え、GUIが更新するレートはrate変数を調整することでprint_label(self)で変更できます。
楽しみ !