0

次の例では、デフォルトで開く[ログ]タブ以外の他のタブに書き込むことができません。

#!/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)で変更できます。

楽しみ !

4

1 に答える 1

0

一つには、Tkinterはスレッドセーフではありません。複数のスレッドからtkウィジェットにアクセスすることはできません。これを行うと、予測できない動作が発生します(ただし、通常、クラッシュが発生することは予測できます)。

もう一つの質問、つまり事前に宣言する方法についてgui.SettingsFrameは、明白なことを述べずに答える方法がわかりません。それを「宣言」するには、それを作成する必要があり、それを作成するには、それを作成するメソッドを呼び出す必要があります。gui.SettingsFrameまた、他のメソッドの引数として使用する前に、これを行う必要があります。

フレームが作成されるメソッドの外部からフレームにラベルを作成するのはなぜですか?Mainこの問題の解決策は、「設定」ラベルの作成をからに移動することだと思いますMkSettings(同様に「進行状況」ラベルと「ログ」ラベルの場合も同様です)。

于 2011-07-09T15:25:28.297 に答える